Sunday, October 30, 2016

Automatic collection of instances of derived classes in C++

Currently I work on application which measures and registers time that I'm spending in particular application. Since I use various of operating systems (Windows at work, and Linux at home), application has to be available on multiple platforms.
Sometimes I don't want to be tracked (e.g. when the screen is locked), so I've defined collection of rules, when tracking should be suspended:
std::vector<std::shared_ptr<Rule>> rules;
rules.push_back(std::make_shared<Rule1>());
rules.push_back(std::make_shared<Rule2>());
rules.push_back(std::make_shared<Rule3>());
rules.push_back(std::make_shared<Rule4>());
However, some of the rules are OS-specific (e.g. Rule2 and Rule3 compile and work only in Windows, and Rule4 makes sense only when app is running on Linux). So what I did:
  • Conditionally enabled files rule2.cc, rule3.cc and rule4.cc in the build system
    if (WIN32)
      set (RULE_FILES rule2.cc rule3.cc)
    elseif (UNIX)
      set (RULE_FILES rule4.cc)
    endif (WIN32)
    
  • Conditionally pushed instances of Rule2, Rule3 and Rule4 to the std::vector.
    std::vector<std::shared_ptr<Rule>> rules;
    rules.push_back(std::make_shared<Rule1>());
    #ifdef _WIN32 // Windows specific rules
      rules.push_back(std::make_shared<Rule2>());
      rules.push_back(std::make_shared<Rule3>());
    #elif __linux__ // Linux specific rules
      rules.push_back(std::make_shared<Rule4>());
    #endif
    
Two steps - sounds redundant. Also, I don't really like using ifdefs in the code (code with ifdefs is more difficult to maintain, and it's less readable). So I've implemented very simple class(actually, two classes), which helps creating a collection of instances of inherited classes.
Here's a code:
#include <memory>
#include <typeindex>
#include <unordered_map>

template <typename Derived, typename Base>
struct Registrar
{
  template<typename ...Args>
  Registrar(Args&&... args)
  {
    Base::registry()[typeid(Derived)] =
      std::make_shared<Derived>(std::forward<Args>(args)...);
  }
};

template<typename T>
struct Registrable
{
  typedef std::unordered_map<std::type_index, std::shared_ptr<T>> collection_t;
  template<typename R>
  using Registrar = Registrar<R, T>;

  static collection_t & registry()
  {
    static collection_t collection;
    return collection;
  }
};

I use std::unordered_map, because I want to make sure that I have only one instance of each class in my collection. If you accept, or even want to have multiple instances of the same class in the collection, you can replace it with std::vector, or any other collection.
And that's how the code above can be used:

class Base : public Registrable<Base>
{
public:
  virtual void print_myself() = 0;
};

class WithParameter : public Base
{
  int x;

public:
  WithParameter(int x) : x(x) {}

  void print_myself() override
  {
    std::cout << "WithParameter(" << x << ")" << std::endl;
  }

  static Registrar<WithParameter> registrar;
};
WithParameter::Registrar<WithParameter> WithParameter::registrar(25);

class NoParameter : public Base
{
public:
  void print_myself() override
  {
    std::cout << "NoParameter" << std::endl;
  }

  static Registrar<NoParameter> registrar;
};
NoParameter::Registrar<NoParameter> NoParameter::registrar;

I've created Base class, and two inherited classes (WithParameter and NoParameter). Both derived classes have static registrar object, which automatically adds instances of the classes to the collection.
Unfortunately, constructor parameters (if any), have to be compile time constants. But in my case, it's not a problem.
Now, we can e.g. enumerate our collection:
for (auto x : Base::registry())
{
  x.second->print_myself();
}

Full code with example can be found on my github's gist [1].

Links
[1] https://gist.github.com/loganek/c0c8dfb30cd72a5a1a47b3701b61d3da

Saturday, July 16, 2016

Generic C++ GObject signals wrapper

Recently I've discovered, that connecting to signals in gstreamermm can be really inconvenient. The problem doesn't exist in the other mm libraries, because most of the classes and their signals are wrapped.
But GStreamer allows to create user-defined elements, so it's actually impossible to wrap everything in gstreamermm (for now library supports wrappers for gstreamer plugins core and base).

Currently, if you want to connect to a signal in gstreamermm, you have two options:
  1. Using pure C API:

  2. auto typefind = Gst::ElementFactory::create_element("typefind");
    
    g_signal_connect (typefind->gobj(), 
                      "have-type", 
                      G_CALLBACK(cb_typefind_havetype), 
                     (gpointer *)typefind->gobj());
    
    static void cb_typefind_havetype (GstTypeFindElement *typefind,
                                      guint               probability,
                                      GstCaps            *caps,
                                      gpointer            user_data)
    {
      // callback implementation
    }
    Well, it's not very bad. But... you have to use C structures in the callback instead of C++ wrappers.

  3. Using gstreamermm API
  4. As I mentioned, gstreamermm provide wrappers for core and base plugins, so some of the elements (and their signals) are already wrapped in the library:
    auto typefind = Gst::TypeFindElement::create();
    typefind->signal_have_type().connect(
        [] (guint probability, const Glib::RefPtr& caps)
        {
          // callback implementation
        });
    
However, many plugins are not wrapped (and they never will), so usually you need to either write wrapper for element which you wanted to use (and then, maintain this wrapper as well), or use pure C API.
Moreover, I'm going to remove plugin API in the next release [1], so user won't be able to use gstreamermm API even for the base and core plugins. I was wondering, if it would be possible to write generic wrapper for the GObject signals. So... there you are! The solution is not perfect yet, and I haven't tested it so much, but so far it works fine with few plugins and signals.

namespace Glib
{  
  template <typename T>
  static constexpr T wrap (T v, bool=true)
  {
    return v;
  }

  template <typename T>
  static constexpr T unwrap (T v, bool=true)
  {
    return v;
  }

  template<typename T>
  using unwrapped_t = decltype(unwrap(*((typename std::remove_reference<T>::type*)nullptr)));

  template<typename T>
  constexpr T return_helper()
  {
    typedef unwrapped_t<T> Ret;
    return Ret();
  }

  template<>
  constexpr void return_helper()
  {
    return void();
  }
}


template<typename>
class signal_callback;

template<typename Ret, typename ...T>
class signal_callback<Ret(T...)>
{
  template<typename ...Args>
  static auto callback(void* self, Args ...v)
  {
    using Glib::wrap;
    typedef sigc::slot< void, decltype(wrap(v))... > SlotType;

    void* data = std::get<sizeof...(Args)-1>(std::tuple<Args...>(v...));

    // Do not try to call a signal on a disassociated wrapper.
    if(dynamic_cast<Glib::Object*>(Glib::ObjectBase::_get_current_wrapper((GObject*) self)))
      {
	try
	  {
	    if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data))
	      {
		(*static_cast<SlotType*>(slot))(wrap(std::forward<Args>(v), true)...);
	      }
	  }
	catch(...)
	  {
	    Glib::exception_handlers_invoke();
	  }
      }

    return Glib::return_helper<Ret>();
  }

public:
  auto operator()(const std::string& signal_name, const Glib::RefPtr<Glib::Object>& obj)
  {
    using Glib::unwrap;
    static std::map<std::pair<GType, std::string>, Glib::SignalProxyInfo> signal_infos;

    auto key = std::make_pair(G_TYPE_FROM_INSTANCE (obj->gobj()), signal_name);
    if (signal_infos.find(key) == signal_infos.end())
      {
	signal_infos[key] = {
	  signal_name.c_str(),
	  (GCallback) &callback<Glib::unwrapped_t<T>..., void*>,
	  (GCallback) &callback<Glib::unwrapped_t<T>..., void*>
	};
      }

    return Glib::SignalProxy<Ret, T... >(obj.operator->(), &signal_infos[key]);
  }
};


auto typefind = Gst::ElementFactory::create_element("typefind"),
signal_callback<void(guint, const Glib::RefPtr<Gst::Caps>&)> signal_wrapper;

signal_wrapper("have-type", typefind).connect(
  [&ready, &cv] (guint probability, const Glib::RefPtr<Gst::Caps>& caps) {
    std::cout << "have-type: probability=" << probability << std::endl;
    Gst::Structure structure = caps->get_structure(0);
    const Glib::ustring mime_type = structure.get_name();
    std::cout << "have-type: mime_type=" << mime_type << std::endl;

    structure.foreach([] (const Glib::QueryQuark& id, const Glib::ValueBase& value) {
      const Glib::ustring str_id = id;
      gchar* str_value = g_strdup_value_contents(value.gobj());
      std::cout << "Structure field: id=" << str_id << ", value=" << str_value << std::endl;
      g_free(str_value);		      
      return true;
  });
});

Full source of the code can be found on the github [2].
As you see, you still have to know the type of the callback, but at least you can use gstreamermm C++ classes.
There is couple of things to do in this code, like getting last parameter from the list in more efficient way than through the tuple, etc.
I don't feel it is stable enough to integrate it with gstreamermm, but probably in the future I'll do that. Also, we could even internally use it in the glibmm to reduce amount of generated code.

Links
[1] https://bugzilla.gnome.org/show_bug.cgi?id=755395
[2] https://gist.github.com/loganek/7833089caff73ff2e8b1f076c8f7910e