2

I use the recursive template idiom to automatically register all children of a base class in a factory. However in my design the child class must have as a friend class the base class. As the Constructor of my Base class should be private to avoid instantiation of this class other than via the factory.

The overal aim would be that the registration of the factory is done in the BaseSolver and the ChildClasses cannot be instantiated other than via the factory.

Here is the code of my base class which automatically registers all children in the SolverFactory.

template<class T>
struct BaseSolver: AbstractSolver
{
protected:
    BaseSolver()
    {
        reg=reg;//force specialization
    }
    virtual ~BaseSolver(){}
    /**
     * create Static constructor.
     */
    static AbstractSolver* create()
    {
        return new T;
    }

    static bool reg;
    /**
     * init Registers the class in the Solver Factory
     */
    static bool init()
    {
        SolverFactory::instance().registerType(T::name, BaseSolver::create);
        return true;
    }
};

template<class T>
bool BaseSolver<T>::reg = BaseSolver<T>::init();

And here the header file of my child class:

class SolverD2Q5 : public BaseSolver<SolverD2Q5>{

private:
  //how can I avoid this?
  friend class BaseSolver;

  SolverD2Q5();

  static const std::string name;

}

This works fine. However I really do not like to have to add the BaseSolver as a friend class, however I do not want the constructor and the static member name to be public.

Is there a more elegant solution or a better layout to avoid this?

Community
  • 1
  • 1
tune2fs
  • 7,605
  • 5
  • 41
  • 57
  • Why does BaseSolver need to be a friend class? Could you add `friend class T` to BaseSolver instead? – Bok McDonagh Nov 07 '12 at 16:43
  • How should that help I do not want to access private methods in the BaseSolver but the BaseSolver should access private members of the Solver. – tune2fs Nov 07 '12 at 16:49
  • I like this design the way it is. Why is the `friend` declaration an issue? – aschepler Sep 10 '13 at 11:21
  • Replacing `private` with `protected` in the `SolverD2Q5` is still **not public** hence replacing `T` in the `BaseSolver` with a wrapper class deriving from `T` solves the problem. My previous answer just gave a hint. Now I have added a full example. I can not see how it is not a solution and is is not even intrusive. And it creates no additional code in the executable. – Patrick Fromberg Dec 03 '17 at 13:07

1 Answers1

0

UPDATE: I believe the trick has not been understood hence I created the complete solution now. It is just one little change to the code of the OP. Just replace T in the BaseSolver with an empty class definition deriving from T.

Original Text: I think you can do it by delegating the friendship to a wrapper class that is private to the base Solver. This class will inherit from any class for which instances are to be created. The compiler should optimize the wrapper class away.

#include <iostream>
#include <map>

struct AbstractSolver { virtual double solve() = 0; };

class SolverFactory
{
    std::map<char const * const, AbstractSolver * (*)()> creators;
    std::map<char const * const, AbstractSolver *> solvers;
public:
    static SolverFactory & instance()
    {
        static SolverFactory x;
        return x;
    }
    void registerType(char const * const name, AbstractSolver *(*create)())
    {
        creators[name] = create;
    }
    AbstractSolver * getSolver(char const * const name)
    {
        auto x = solvers.find(name);
        if (x == solvers.end())
        {
            auto solver = creators[name]();
            solvers[name] = solver;
            return solver;
        }
        else
        {
            return x->second;
        }
    }
};

template<class T> class BaseSolver : public AbstractSolver
{
    struct Wrapper : public T { // This wrapper makes the difference
        static char const * const get_name() { return T::name; }
    };
protected:
    static bool reg;
    BaseSolver() {
        reg = reg;
    }
    virtual ~BaseSolver() {}
    static T * create() {
        return new Wrapper; // Instantiating wrapper instead of T
    }
    static bool init()
    {
        SolverFactory::instance().registerType(Wrapper::get_name(), (AbstractSolver * (*)())BaseSolver::create);
        return true;
    }
};

template<class T>
bool BaseSolver<T>::reg = BaseSolver<T>::init();

struct SolverD2Q5 : public BaseSolver<SolverD2Q5>
{
public:   
    double solve() { return 1.1; }
protected: 
    SolverD2Q5() {} // replaced private with protected
    static char const * const name;
};
char const * const SolverD2Q5::name = "SolverD2Q5";

struct SolverX : public BaseSolver<SolverX>
{
public:    
    double solve() { return 2.2; }
protected: 
    SolverX() {} // replaced private with protected
    static char const * const name;
};
char const * const SolverX::name = "SolverX";

int main()
{
    std::cout << SolverFactory::instance().getSolver("SolverD2Q5")->solve() << std::endl;
    std::cout << SolverFactory::instance().getSolver("SolverX")->solve() << std::endl;
    std::cout << SolverFactory::instance().getSolver("SolverD2Q5")->solve() << std::endl;
    std::cout << SolverFactory::instance().getSolver("SolverX")->solve() << std::endl;

    char x;
    std::cin >> x;
    return 0;
}
Patrick Fromberg
  • 1,313
  • 11
  • 37