1

I have c++ library with code following code structure:

#include <vector>

std::vector<Base*> UserClasses;

int main(int argc,char** argv)
{
    Init(argc,argv)
    while(true)
    {
       for(auto* class : UserClasses){
          class->start();
       }
       DoStuff();
       for(auto* class : UserClasses){
          class->update();
       }
       DoStuff();
    }
    Shutdown();
}

Base class is interface class with virtual methods.Like this:

class Base
{
public:
    Base();
    virtual ~Base();

    virtual void start() = 0;
    virtual void update() = 0;
};

The user will write his own classes like this:

class MyClass : public Base
{
public:
    MyClass ();
    ~MyClass ();

    virtual void start() override
    {
        //userstaff
    };

    virtual void update() override
    {
        //userstaff
    };
};

I want to allow user to include my library and headers in his project, write his own child classes and compile project into executable.

But how can my library create instance of user classes?

It doesn't know anything about them.

Is it possible somehow create instance of class within header file and push it to vector?

I saw that it can be done by initializing static variables because it happens before main but I don't understand how to implement it.

  • First of all `class` is a keyword, you can't use a keyword for variable names. Secondly, `UserClasses` is a vector of *integers*. Thirdly `Init(argc,char** argv)`? That won't work. And fourthly, I don't see you attempting to populate the vector anyway. All in all, it seems you have skipped major parts of your text-books or classes. And if you don't have any books then you really need to invest in [some good ones](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list), which you should read from the very start. – Some programmer dude Jul 25 '21 at 12:37
  • 9
    If I were the user of your library, I'd be very annoyed at having a custom `main` forced on me. – HolyBlackCat Jul 25 '21 at 12:42
  • Your `Base` class is flawed in that it lacks a virtual destructor. – PaulMcKenzie Jul 25 '21 at 12:50
  • @Someprogrammerdude thanks for finding mistakes, i edited my question. But the question is more about the way to integrating side classes into library with main fucntion. The word classes is used in names of variables and classes to show that this will be user class and vector of them. Now i am looking for way to create instance and push this instance to vector with implementation from question. – ОНтон ОНтон Jul 25 '21 at 12:51
  • 1
    It looks like you are trying to create a plugin system. Your users are supposed to write plugins. It should not be different from any other plugin system. Your users put their plugins in a DLL/shared library and arrange for a function with a fixed, pre-agreed name to return a pointer to an instance of their plugin object. – n. m. could be an AI Jul 25 '21 at 12:57

3 Answers3

1

As properly mentioned in the comments, do not define main in a library. Also, avoid non-const global variables, since this is a bad thing (tm). Instead, define a function and pass UserClasses to it directly. And it is better to encapsulate all the state you have in a class:

class Main {
public:
  Main(int argc, char** argv) {
    Init(argc, argv);
  }

  int run(const std::vector<std::unique_ptr<Base>>& user_classes)
  {
      while(true)
      {
        for(const auto& class : user_classes){
            class->start();
        }
        DoStuff();
        for(const auto& class : user_classes){
            class->update();
        }
        DoStuff();
      }    
      Shutdown();
  }

private:
  void DoStuff();
  void Shutdown();

  // ...
};
Mikhail
  • 20,685
  • 7
  • 70
  • 146
1

But how can my library create instance of user classes?

You can't but you can provide the user with a convenient way of adding a instance. You could e.g. provide a template class that registers an instance of a class in the constructor which allows the user to add an instance by simply defining an instance of this class.

Example

(Static) Library

Base.h

int main(int, char**);

template<typename T>
class BaseRegistrar
{
public:
    BaseRegistrar();
};

class Base
{
public:
    Base();
    virtual ~Base();

    virtual void start() = 0;
    virtual void update() = 0;

private:
    static std::vector<std::unique_ptr<Base>>& GetBaseInstances();

    friend int main(int, char**);

    template<typename T>
    friend class BaseRegistrar;
};

template<typename T>
BaseRegistrar<T>::BaseRegistrar()
{
    Base::GetBaseInstances().emplace_back(new T);
}

Base.cpp

#include "Base.h"

Base::Base(){}
Base::~Base(){}

std::vector<std::unique_ptr<Base>>& Base::GetBaseInstances()
{
    static std::vector<std::unique_ptr<Base>> instances;
    return instances;
}

main.cpp

#include "Base.h"

int main(int argc,char** argv)
{
    for(auto& clazz : Base::GetBaseInstances()){
        clazz->start();
    }

    for(auto& clazz : Base::GetBaseInstances()){
        clazz->update();
    }

}

Executable

MyClass.cpp

#include "Base.h"

#include <iostream>

class MyClass : public Base
{
public:
    MyClass ()
    {}
    ~MyClass ()
    {}

    virtual void start() override
    {
        std::cout << "MyClass::start()\n";
    }

    virtual void update() override
    {
        std::cout << "MyClass::update()\n";
    }
};

// adds an instance of the class to the list
BaseRegistrar<MyClass> registrar;

Output

MyClass::start()
MyClass::update()
fabian
  • 80,457
  • 12
  • 86
  • 114
1

it can be done by initializing static variables

Yes, here's an example.

This is similar to @fabian's answer, but:

  • You don't need to manually register the classes.
  • The classes are not constructed automatically, you do it when you need them.

run on gcc.godbolt.org

#include <cstddef>
#include <iostream>
#include <memory>
#include <vector>

class BaseLow
{
    template <typename T> friend class Base;
    using factory_func_t = std::unique_ptr<BaseLow>(*)();
    static std::vector<factory_func_t> &GetFactoryFuncsMutable()
    {
        static std::vector<factory_func_t> ret;
        return ret;
    }

  public:
    virtual ~BaseLow() {}
    virtual void foo() = 0;

    static std::vector<factory_func_t> &GetFactoryFuncs()
    {
        return GetFactoryFuncsMutable();
    }
};

template <typename T>
class Base : public BaseLow
{
    static std::nullptr_t RegisterSelf()
    {
        GetFactoryFuncsMutable().push_back([]() -> std::unique_ptr<BaseLow>
        {
            return std::make_unique<T>();
        });
        return nullptr;
    }

    inline static const std::nullptr_t dummy = RegisterSelf();

    // Force `dummy` to be instantiated.
    static constexpr std::integral_constant<decltype(&dummy), &dummy> dummy_helper{};
};

struct A : Base<A>
{
    void foo() override
    {
        std::cout << "I'm A!\n";
    }
};

struct B : Base<B>
{
    void foo() override
    {
        std::cout << "I'm B!\n";
    }
};

int main()
{
    std::vector<std::unique_ptr<BaseLow>> objects;
    for (const auto &func : BaseLow::GetFactoryFuncs())
        objects.push_back(func());

    for (auto &obj : objects)
        obj->foo();
}
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207