Лучший способ для типов C ++ для самостоятельной регистрации в списке? - PullRequest
10 голосов
/ 31 декабря 2008

Предположим, у меня есть данные для каждого класса: (AandB.h)

class A
{
public:
   static Persister* getPersister();
}

class B
{
public:
   static Persister* getPersister();
}

... и еще много-много классов. И я хочу сделать что-то вроде:

persistenceSystem::registerPersistableType( A::getPersister() );
persistenceSystem::registerPersistableType( B::getPersister() );
...
persistenceSystem::registerPersistableType( Z::getPersister() );

... для каждого класса.

Мой вопрос: есть ли способ автоматизировать создание списка данных для каждого типа, чтобы мне не приходилось перечислять каждый тип в большом фрагменте (как в примере выше)?

Например, один из способов сделать это: (AutoRegister.h)

struct AutoRegisterBase
{
   virtual ~AutoRegisterBase() {}
   virtual void registerPersist() = 0;
   static AutoRegisterBase*& getHead()
   {
      static AutoRegisterBase* head= NULL;
      return head;
   }

   AutoRegisterBase* next;
};

template <typename T>
struct AutoRegister : public AutoRegisterBase
{
   AutoRegister() { next = getHead(); getHead() = this; }

   virtual void registerPersist()
   {
       persistenceSystem::registerPersistableType( T::getPersister() );
   }
};

и используйте это следующим образом: (AandB.cxx:)

static AutoRegister<A> auto_a;
static AutoRegister<B> auto_b;

Теперь, после запуска моей программы, я могу безопасно выполнить: (main.cxx)

int main( int, char ** )
{
    AutoRegisterBase* p = getHead();
    while ( p )
    {
        p->registerPersist();
        p = p->next;
    }
    ...
}

чтобы собрать каждый фрагмент данных для каждого типа и зарегистрировать их в большом списке где-нибудь для последующего использования.

Проблема этого подхода заключается в том, что мне требуется добавить объект AutoRegister где-то для каждого типа. (то есть он не очень автоматический и его легко забыть сделать). А как насчет шаблонных классов? Что мне действительно нужно, так это чтобы экземпляр класса шаблона каким-то образом заставлял этот класс автоматически регистрироваться в списке. Если бы я мог сделать это, я бы избежал необходимости иметь пользователя класса (а не автора), чтобы не забыть создать:

static AutoRegister< SomeClass<X1> > auto_X1;
static AutoRegister< SomeClass<X2> > auto_X2;
...
etc....

для каждого экземпляра класса шаблона.

Что касается FIW, я подозреваю, что для этого нет решения.

Ответы [ 5 ]

8 голосов
/ 31 декабря 2008

Вы можете выполнить что-то перед main один раз, если сделана реализация шаблона. Хитрость заключается в том, чтобы поместить статический член данных в шаблон класса и ссылаться на него извне. Побочный эффект от того, что статические члены-триггеры могут использоваться для вызова функции регистра:

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;

Получите класс, который вы хотите автоматически зарегистрировать с automatic_register<YourClass>. Функция register будет вызываться перед main, когда создается экземпляр referrer (что происходит, когда этот класс является производным, что неявно создает экземпляр этого класса из шаблона).

Наличие некоторой тестовой программы (вместо функции регистра вызывается функция do_it):

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

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

Получает этот вывод (как и ожидалось):

doit main
2 голосов
/ 31 декабря 2008

Зарегистрируйте каждый шаблон во время выполнения в конструкторе. Используйте статическую переменную для каждого шаблона, чтобы проверить, был ли тип уже зарегистрирован. Ниже приведен пример быстрого взлома:

#include <iostream>
#include <vector>

using namespace std;

class Registerable {
    static vector<Registerable *> registry_;

public:
    static void registerFoo(Registerable *p)
    {
        registry_.push_back(p);
    }

    static void printAll()
    {
        for (vector<Registerable *>::iterator it = registry_.begin();
             it != registry_.end(); ++it)
            (*it)->print();
    }

    virtual void print() = 0;
};

vector<Registerable *> Registerable::registry_;

template <typename T>
class Foo : public Registerable {
    static bool registered_;

public:
    Foo()
    {
        if (!registered_) {
            registerFoo(this);
            registered_ = true;
        }
    }

    void print()
    {
        cout << sizeof (T) << endl;
    }
};

template <typename T> bool Foo<T>::registered_ = false;

int
main(int argc, char *argv[])
{
    Foo<char> fooChar;
    Foo<short> fooShort;
    Foo<int> fooInt;

    Registerable::printAll();

    return 0;
}

Он должен выводить размер каждого параметра шаблона в том порядке, в котором были созданы классы:

1
2
4

Эта версия удаляет регистрационный код из каждого конструктора и помещает его в базовый класс.

#include <iostream>
#include <vector>

using namespace std;

class Registerable {
    static vector<Registerable *> registry_;

public:
    static void registerFoo(Registerable *p)
    {
        registry_.push_back(p);
    }

    static void printAll()
    {
        for (vector<Registerable *>::iterator it = registry_.begin();
             it != registry_.end(); ++it)
            (*it)->print();
    }

    virtual void print() = 0;
};

vector<Registerable *> Registerable::registry_;

template <typename T>
class Registerer : public Registerable {
    static bool registered_;

public:
    Registerer(T *self)
    {
        if (!registered_) {
            registerFoo(self);
            registered_ = true;
        }
    }
};

template <typename T> bool Registerer<T>::registered_ = false;

template <typename T>
class Foo : public Registerer<Foo<T> > {
public:
    Foo() : Registerer<Foo<T> >(this) { }

    void print()
    {
        cout << sizeof (T) << endl;
    }
};

int
main(int argc, char *argv[])
{
    Foo<char> fooChar;
    Foo<short> fooShort;
    Foo<int> fooInt;

    Registerable::printAll();

    return 0;
}

Я добавил пример другого не шаблонного класса, используя реестр. Итак, окончательный результат будет:

foo: 1
foo: 2
foo: 4
bar
0 голосов
/ 17 апреля 2017

Использование статических блоков на уровне файлов для выполнения различных регистраций

Статический блок? Что это?

Статический блок - это блок кода (то есть код между фигурными скобками, который определяет область действия), который выполняется за некоторое время до выполнения main(). В Java есть эта функция , и в C ++ она есть -

Что за разговор, Уиллис? C ++ не имеет статических блоков!

Нет, действительно, в C ++ есть статические блоки. Вам просто нужно, скажем, «разоблачить» их существование .

Хм. Любопытно. И как статические блоки помогают моей проблеме с регистрацией?

Это действительно очень просто. Сразу после определения класса A вы регистрируете его так:

class A { /* ... whatever ... */ };

static_block {
    persistenceSystem::registerPersistableType(A::getPersister());
}

Однако есть одна оговорка: статические блоки могут быть частью фиаско статического порядка инициализации вместе с любой статически инициализированной частью вашей персистентной системы; поэтому вы должны убедиться, что эти статические блоки могут работать до (большей части) другой статики; и что все персистеры разных классов могут быть зарегистрированы в произвольном порядке.

0 голосов
/ 31 декабря 2008

Литб ответ выглядит как отличный. Единственный главный недостаток для меня это "(void) register_object;" в конструкторе automatic_register ():

  1. Полагаю, это требует дополнительного поиска в памяти при каждом вызове конструктора foo. (= превышение скорости.)
  2. Мне было бы любопытно узнать, убирают ли очень агрессивные оптимизаторы этот поиск.

Есть также несколько странных моментов поведения, когда я это пробовал (g ++ 4.2.4). В приведенном ниже примере, если я закомментирую конструкторы в bar и foo, ни один из них не будет зарегистрирован. Если присутствуют оба конструктора, несмотря на то, что ни один конструктор никогда не вызывается, оба класса регистрируются. Не имеет значения, если распечатка находится в конструкторе.

Что-то связанное с правилами конструктора типа POD? (Я пытался сделать foo не POD-типом, добавив деструктор. Казалось, без разницы.)

Если я создаю экземпляр любого класса в main, C ++ предоставляет конструктору вызовы базового конструктора и автоматически регистрирует объекты.

#include <iostream>

template<typename D>
struct automatic_register
{
    automatic_register() 
    {
       // reference it
       (void) register_object;
    }
private:
    struct exec_register
    {
       exec_register() 
       {
          std::cout << "Register: " << D::getName() << std::endl;
       }
    };

    static exec_register register_object;
};

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


class foo : automatic_register<foo>
{ 
public:
#if 0
   foo() { std::cout << " foo::foo()"; } 
#endif

   virtual void x() {}
   static char const* getName()
   {
      return "foo";
   } 

   int i;
}; 

class bar : automatic_register<bar>
{ 
public:
#if 0
   bar() { std::cout << " bar::bar()";  } 
#endif

   static char const* getName()
   {
      return "bar";
   } 

   foo f;
}; 

int main(int, char **t )
{
    std::cout << " main "; 
}
0 голосов
/ 31 декабря 2008

Решение Registerable является изящной идеей, но имеет несколько проблем. В идеале я бы не хотел добавлять код в конструктор:

  1. Поскольку он основан на вызове конструктора для регистрации типа, это маленькая случайность о том, что регистрируется, а что нет. Для таких вещей, как постоянство, я никогда не могу вызвать конструктор определенного типа перед использованием списка, но мне могут понадобиться данные типа в списке, чтобы знать, как сохранить объект в файле.

  2. Стоимость вызова во время вызова конструктора. Я хотел бы загрузить фронт стоимость времени и не оплачивать стоимость много раз. Если бы я имел вектор этих объектов и изменил размер vector я бы оплачивал стоимость времени каждый раз, когда вызывался конструктор копирования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...