Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Several C++ classes need to use the same static method with a different implementation

I need several C++ classes to have a static method "register", however the implementation of register varies between those classes.

It should be static because my idea is to "register" all those classes with Lua (only once of course).

Obviously I can't declare an interface with a static pure virtual function. What do you guys suggest me to do ? Simplicity is welcome, but I think some kind of template could work.

Example of what I would like to achieve

class registerInterface
{
public:
    static virtual void register() = 0; //obviously illegal
};

class someClass: public registerInterface
{
    static virtual void register()
    {
        //I register myself with Lua
    }
}

class someOtherClass: public registerInterface
{
    static virtual void register()
    {
        //I register myself with Lua in a different way

    }
}

int main()
{
    someClass::register();
    someOtherClass::register();

    return 0;
}
like image 465
Goles Avatar asked Dec 28 '22 08:12

Goles


2 Answers

Based on how you've described the problem, it's unclear to me why you even need the 'virtual static method' on the classes. This should be perfectly legal.

class SomeClass {
  static void register(void) {
    ...
  }
}

class SomeOtherClass {
  static void register(void) {
    ...
  }
}

int main(int argc, char* argv[]) {
  SomeClass::register();
  SomeOtherClass::register();

  return 0;
}

Drop the RegisterInterface, I don't think you need it.

like image 122
Hitesh Avatar answered Jan 05 '23 16:01

Hitesh


If it helps, you could take Hitesh's answer, and add:

struct luaRegisterManager {
    template <typename T>
    void registrate() {
        T::registrate();
        // do something else to record the fact that we've registered - 
        // perhaps "registrate" should be returning some object to help with that
    }
};

Then:

int main() {
    luaRegisterManager lrm;
    lrm.registrate<someClass>();
    lrm.registrate<someOtherClass>();
}

More generally, if you want to introduce any dynamic polymorphism in C++, then you need an object, not just a class. So again, perhaps the various register functions should be returning objects, with some common interface base class registeredClass, or classRegistrationInfo, or something along those lines.

Could provide an example of what you feel it is that you need dynamic polymorphism for? Hitesh's code precisely matches your one example, as far as I can see, so that example must not cover all of your anticipated use cases. If you write the code that would be using it, perhaps it will become clear to you how to implement it, or perhaps someone can advise.

Something else that might help:

#include <iostream>
#include <string>
#include <vector>

struct Registered {
    virtual std::string name() = 0;
    virtual ~Registered() {}
    Registered() {
        all.push_back(this);
    }
    static std::vector<Registered*> all;
};

std::vector<Registered*> Registered::all;
typedef std::vector<Registered*>::iterator Iter;

template <typename T>
struct RegisteredT : Registered {
    std::string n;
    RegisteredT(const std::string &name) : n(name) { T::registrate(); }
    std::string name() { return n; }
    // other functions here could be implemented in terms of calls to static
    // functions of T.
};

struct someClass {
    static Registered *r;
    static void registrate() { std::cout << "registering someClass\n"; }
};
Registered *someClass::r = new RegisteredT<someClass>("someClass");

struct someOtherClass {
    static Registered *r;
    static void registrate() { std::cout << "registering someOtherClass\n"; }
};
Registered *someOtherClass::r = new RegisteredT<someOtherClass>("someOtherClass");

int main() {
    for (Iter it = Registered::all.begin(); it < Registered::all.end(); ++it) {
        std::cout << (*it)->name() << "\n";
    }
}

There are all sorts of problems with this code if you try to split it across multiple compilation units. Furthermore, this kind of thing leads to spurious reports from memory leak detectors unless you also write some code to tear everything down at the end, or use a vector of shared_ptr, Boost pointer vector, etc. But you see the general idea that a class can "register itself", and that you need an object to make virtual calls.

In C++ you usually try to avoid static initialisation, though, in favour of some sort of setup / dependency injection at the start of your program. So normally you would just list all the classes you care about (calling a function on each one) rather than try to do this automatically.

like image 31
Steve Jessop Avatar answered Jan 05 '23 15:01

Steve Jessop