Somehow Register My Classes in a List

Somehow register my classes in a list

Here is method to put classes names inside a vector. Leave a comment if I missed important details. I don't think it will work for templates, though.

struct MyClasses {
static vector<string> myclasses;
MyClasses(string name) { myclasses.push_back(name); }
};

#define REGISTER_CLASS(cls) static MyClasses myclass_##cls(#cls);

struct XYZ {
};

REGISTER_CLASS(XYZ);

The trick here is to make some computation before main() is called and you can achieve this via global initialization. REGISTER_CLASS(cls) actually generates code to call the constructor of MyClasses at program startup.

UPDATE: Following gf suggestion you can write this:

#define REGISTER_CLASS(cls) temp_##cls; static MyClasses myclass_##cls(#cls); class cls
class REGISTER_CLASS(XYZ) { int x, y, z; }

Best way to for C++ types to self register in a list?

You can execute something before main once if a instantiation of a template is made. The trick is to put a static data member into a class template, and reference that from outside. The side effect that static data member triggers can be used to call the register function:

template<typename D>
struct automatic_register {
private:
struct exec_register {
exec_register() {
persistenceSystem::registerPersistableType(
D::getPersister()
);
}
};
// will force instantiation of definition of static member
template<exec_register&> struct ref_it { };

static exec_register register_object;
static ref_it<register_object> referrer;
};

template<typename D> typename automatic_register<D>::exec_register
automatic_register<D>::register_object;

Derive the class you want to be auto-registered from automatic_register<YourClass> . The register function will be called before main, when the declaration of referrer is instantiated (which happens when that class is derived from, which will implicitly instantiate that class from the template).

Having some test program (instead of the register function, a function do_it is called):

struct foo : automatic_register<foo> {    
static void do_it() {
std::cout << " doit ";
}
};

int main() {
std::cout << " main ";
}

Yields this output (as expected):

doit main

Can a factory somehow determine all possible classes, by looking in a particular file?

You can write a parser and have the class process the input file, but I doubt that's what you want.

You can also use the crtp pattern to your advantage:

template<class T>
struct Base
{
Base()
{
reg;
}
virtual std::string name() = 0;
static bool reg;
static bool init()
{
T t;
Factory::registerClass(t.name());
return true;
}
};

template<class T>
bool Base<T>::reg = Base<T>::init();

source

You'd then derive classes as

struct Derived1 : Base<Derived1> 
{
...
}

This would automatically register your class with Factory given its name (you have to implement name in non-abstract classes because it's pure).

Additionally, you can pass a callback to registerClass that knows how to create your derived class.

Scalable automatic class registration in C++

Writing Java code in C++ rarely works well. All those heap allocations are probably what's killing performance (as they would in Java, but Java startup is so slow that nobody would notice). Use static objects, and don't put a Registrar object into each generated class; that's just a waste of time and space.

Instantiating classes by name with factory pattern

Here is a generic factory example implementation:

template<class Interface, class KeyT=std::string>
struct Factory {
typedef KeyT Key;
typedef std::auto_ptr<Interface> Type;
typedef Type (*Creator)();

bool define(Key const& key, Creator v) {
// Define key -> v relationship, return whether this is a new key.
return _registry.insert(typename Registry::value_type(key, v)).second;
}
Type create(Key const& key) {
typename Registry::const_iterator i = _registry.find(key);
if (i == _registry.end()) {
throw std::invalid_argument(std::string(__PRETTY_FUNCTION__) +
": key not registered");
}
else return i->second();
}

template<class Base, class Actual>
static
std::auto_ptr<Base> create_func() {
return std::auto_ptr<Base>(new Actual());
}

private:
typedef std::map<Key, Creator> Registry;
Registry _registry;
};

This is not meant to be the best in every circumstance, but it is intended to be a first approximation and a more useful default than manually implementing the type of function stijn mentioned. How each hierarchy should register itself isn't mandated by Factory, but you may like the method gf mentioned (it's simple, clear, and very useful, and yes, this overcomes the inherent problems with macros in this case).

Here's a simple example of the factory:

struct Base {
typedef ::Factory<Base> Factory;
virtual ~Base() {}
virtual int answer() const = 0;

static Factory::Type create(Factory::Key const& name) {
return _factory.create(name);
}
template<class Derived>
static void define(Factory::Key const& name) {
bool new_key = _factory.define(name,
&Factory::template create_func<Base, Derived>);
if (not new_key) {
throw std::logic_error(std::string(__PRETTY_FUNCTION__) +
": name already registered");
}
}

private:
static Factory _factory;
};
Base::Factory Base::_factory;

struct A : Base {
virtual int answer() const { return 42; }
};

int main() {
Base::define<A>("A");
assert(Base::create("A")->answer() == 42);
return 0;
}


Related Topics



Leave a reply



Submit