Отслеживать типы объектов - PullRequest
0 голосов
/ 14 марта 2019

Я пытаюсь добиться того, чтобы отслеживать, какие типы созданных нами объектов наследуют от базового класса. Если класс наследует от базового класса, но не создан в объекте, я не очень заинтересован в отслеживании этого (это условие может быть включено или нет в зависимости от того, проще реализация или нет)

Пустой пример:

template <typename T>
class Person
{
public:
    Person() {
        T* x;
        container.push_back(x);
    }
    virtual ~Person() {}
private:
    static heterogeneous_container container;
};

class Employee : public Person <Employee>
{
};

class Employee2 : public Employee
{
};

Кроме того, я хотел бы, чтобы это работало для цепного наследования. Возможно ли, что когда я создаю экземпляр Employee2, базовый класс Person добавит указатель типа Employee2 в контейнер?

Что касается гетерогенного контейнера, я думаю, что это можно использовать ссылка

Ответы [ 3 ]

3 голосов
/ 14 марта 2019

Я думаю, что вы больше хотите:

class Person
{
public:
    Person() {
        objects.push_back(this);
    }
    virtual ~Person() {
        objects.erase(this);
    }
private:
    static std::set<const Person*> objects;
};

class Employee : public Person
{
};

class Employee2 : public Employee
{
};

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

Обратите внимание, что набор objects должен содержать указатели, не type_info для каждого объекта.Проблема в том, что внутри конструктора для подобъекта Person объекта Employee2 наиболее производный тип *this будет Person, а не Employee2 (он не станет Employee2пока выполнение не войдет в конструктор Employee2).

1 голос
/ 14 марта 2019

Более или менее, у меня где-то работает так:

#include <iostream>
#include <functional>
#include <vector>

struct ClassEntry {
    size_t id = 0;
    const char* label;
};

class BaseClass {
public:
protected:

    static void RegisterType(size_t id, const char * label) {
        ClassEntry entry;
        entry.id = id;
        entry.label = label;
        mRegisteredTypes.emplace_back(entry);

        std::cout << "Registered type " << id << " label " << label << std::endl;
    }

    static size_t createId() {
        static size_t id = 0;
        return id++;
    }

    static std::vector<ClassEntry> mRegisteredTypes;
};

std::vector<ClassEntry> BaseClass::mRegisteredTypes;


class OneTimeCall {
public:
    OneTimeCall(std::function<void(void)>&& func)  {
        func();
    }
    virtual ~OneTimeCall() {

    }
};


template<typename T>
class MyClass : public BaseClass {
public:
    MyClass() {
        static OneTimeCall one_time {
            [this]{
                BaseClass::RegisterType(GetId(), T::GetType());
            }
        };


    }
private:

protected:

    static size_t GetId() {
        static size_t id = BaseClass::createId();
        return id;
    }
};


class A : public MyClass<A> {
public:
    A() {

    }

   static const char *GetType() {
        return "ClassA";
   }
};

class B : public MyClass<B> {
public:
    B() {

    }

    static const char *GetType() {
        return "ClassB";
    }
};


int main() {

    A a;
    B b;
    A a2;
    B b2;


    return 0;
}

Вывод:

Registered type 0 label ClassA
Registered type 1 label ClassB

Основная идея заключается в использовании CRTP и статической инициализации в конструкции для регистракаждый тип только один раз.В Linux это работает без проблем, в компиляторе Windows статический идентификатор BaseClass является новым для каждой библиотеки DLL, поэтому вам нужно немного настроить для использования во внешней библиотеке.

При таком подходе вам не нужна какая-либо внешняя библиотека, и ее можно компилировать без rtti.

Для наследования вы можете создать новый класс:

template<typename Current, typename Base>
class Mix : public MyClass<Current>, public Base {};

Таким образом, если вы передадите «тип C» в качестве текущего типа (CRTP), а тип A в качестве базового класса, может работать.

class C : public Mix<C, A> {
public:
    C() {

    }

    static const char *GetType() {
        return "ClassC";
    }
};

При таком подходе, если вы ранее зарегистрировали «A», он не будет зарегистрирован снова, а если у вас нет «A», он будет зарегистрирован после «C».

0 голосов
/ 14 марта 2019

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

#include <iostream>
#include <boost/intrusive/list.hpp>

namespace bi = boost::intrusive;

template<class T>
class Tracker : public bi::list_base_hook<bi::link_mode<bi::auto_unlink>>
{
protected:
    static bi::list<Tracker, bi::constant_time_size<false>> objects_;

    Tracker() noexcept { objects_.push_back(*this); }
    Tracker(Tracker const&) noexcept { objects_.push_back(*this); }

public:
    static auto count() noexcept { return objects_.size(); }
};

template<class T>
bi::list<Tracker<T>, bi::constant_time_size<false>> Tracker<T>::objects_;

struct Employee : Tracker<Employee> {};
struct Employee2 : Employee {};

int main() {
    std::cout << Tracker<Employee>::count() << '\n';
    {
        Employee e0;
        Employee2 e1;
        std::cout << Tracker<Employee>::count() << '\n';
    }
    std::cout << Tracker<Employee>::count() << '\n';
}

Выходы:

0
2
0

Tracker без библиотеки повышения:

struct AutoListNode {
    AutoListNode *next_ = this, *prev_ = this;

    AutoListNode() noexcept = default;
    AutoListNode(AutoListNode const&) = delete;
    AutoListNode& operator=(AutoListNode const&) = delete;
    ~AutoListNode() noexcept { this->erase(); }

    void push_back(AutoListNode* node) noexcept {
        auto prev = prev_;
        node->prev_ = prev;
        node->next_ = this;
        prev->next_ = node;
        prev_ = node;
    }

    void erase() noexcept {
        auto next = next_;
        auto prev = prev_;
        prev->next_ = next;
        next->prev_ = prev;
    }

    size_t size() const noexcept {
        size_t count = 0;
        for(auto node = next_; node != this; node = node->next_)
            ++count;
        return count;
    }
};

template<class T>
class Tracker : private AutoListNode
{
protected:
    static AutoListNode objects_;

    Tracker() noexcept { objects_.push_back(this); }
    Tracker(Tracker const&) noexcept { objects_.push_back(this); }

public:
    static auto count() noexcept { return objects_.size(); }
};

template<class T>
AutoListNode Tracker<T>::objects_;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...