After24
After24

Reputation: 47

Automated call to class static functions C++11

I'm working on a project executed on an ESP32 micro controller (c++11) and I would like to implement a class method that could take Class References (not instances of those classes) as arguments in order to execute static methods of those classes.

I manage to implement the following working solution :

#include <iostream>

    class Model
{
public:
    static void foo()
    {
        std::cout << "Model.foo invoked" << std::endl;
    };
};

class ModelA: public Model
{
public:
    static void foo()
    {
        std::cout << "ModelA.foo invoked" << std::endl;
    };
};

class ModelB: public Model
{
public:
    static void foo()
    {
        std::cout << "ModelB.foo invoked" << std::endl;
    };
};


class Manager
{
public:

    template <class T = Model>
    void callFoo()
    {
        T::foo();
    }

    template<class T = Model, class ... args>
    void setup()
    {
       callFoo<T>();
       callFoo<args...>();
    }

};


int main()
{

    Manager manager;
    manager.setup<ModelA, ModelB>();

    return 0;
}

The output meets the expected result :

ModelA.foo invoked

ModelB.foo invoked

But I have the feeling that this approach is at best a poor hack...

Does someone has a better way to implement this (ideally without using a template) ?

Thank you in advance.

Vincent.

Upvotes: 0

Views: 570

Answers (2)

umlcat
umlcat

Reputation: 4143

You want to use typeclasses as parameters. Not currently available in "C++".

Yet, for your specific case, since all static methods have the same function prototype, or function signature, you could do some workarounds.

First, change each static method into a virtual method:

#include <iostream>

class Model
{
  public:
    virtual Foo(void* Args)
    {
      str::cout << "method Model.DoFoo( ) invoked. << std::endln;
    } // DoFoo

}; // class Model

class ModelA: Model
{
  public:
    virtual Foo(void* Args)
    {
      str::cout << "method ModelA.DoFoo( ) invoked. << std::endln;
    } // DoFoo

}; // class ModelA

class ModelB: Model
{
  public:
    virtual Foo(void* Args) override
    {
      str::cout << "method ModelB.DoFoo( ) invoked << std::endln;
  } // DoFoo

}; // class ModelB

Second, add a local function, not a method, to generate an instance of each class as a result:

 Model* ModelFactory( )
 {
   Model* M = new Model ( );
   return M;
 } // ModelFactory

 Model* ModelAFactory( )
 {
   Model* M = new ModelA( );
   return M;
 } // ModelFactory

 Model* ModelBFactory( )
 {
   Model* M = new ModelB ( );
   return M;
 } // ModelFactory

Three, your Manager class, instead of "variadic" or "..." parameters, you will add a vector as a single parameter, to store references to those factory functions.

And a parameter to store that vector.

#include <vector>

typedef
   Model* (*Factory) (void* Args);

class Manager
{
  public:
     std::vector<Factory>* Factories;

    void setup(std::vector<Factory>* AFactories)
    {
      this.Factories = AFactories;
    } // setup

} ; // class Manager

Note that the factories work as typeclasses parameters.

Four, a method to the Manager class to retrieve each factory from the vector, execute it, generate an instance, call the replacement virtual method, and later, dispose the instance.

class Manager
{
  public:
     std::vector<Factory>* Factories;

    void setup(std::vector<Factory>* AFactories)
    {
      this.Factories = AFactories;
    } // setup

    void run(void* Args)
    {
      for (Factory EachFactory : this.Factories)
      {
         Model* M = EachFactory( );
           M->Foo(Args);
         delete M;
      } // for
    } // void

} ; // class Manager

Five, build the example:

int main( )
{
  Manager* M = new Manager( );

  // prepare compiletime list of classes
  std::vector<Factory>* Factories =
    new std::vector<Factory>*(2);

  Factories->insert(&ModelAFactory);
  Factories->insert(&ModelBFactory);

  // assign list to Manager
  M->setup(Factories);

  // execute each class method
  // with same parameters
  M->run( nullptr );

  // drop list
  delete Factories ( );

  delete M;
} // main

Your Model class and subclasses may have other operations, not related at all, and still do not conflict, with the static methods replaced by virtual methods.

Note, that newer versions of C++ standard can do something similar to this with concepts, and variadic arguments' templates.

Upvotes: 0

bitmask
bitmask

Reputation: 34628

If you want to dispatch static member functions of any type based on references of that type, you can easily do this by (slightly) abusing function template parameter inference:

template <typename T>
void call(T const& = *static_cast<T const*>(nullptr)) {
  T::foo();
}

struct Magic {
  static void foo() {}
};
struct MoreMagic {
  static void foo() {}
};

int main() {
  call<Magic>();
  call(MoreMagic());
  MoreMagic m;
  call(m);
}

Upvotes: 1

Related Questions