Class interface storage specifier (static/non static) as template argument to avoid creating to separate classes

Lets say I have a generic wrapper class for an object called worker with many methods:

template<typename T>
class Wrapper
{
private:
    T m_Worker;

public:
    void DoSomeWork1() { m_Worker.Work1(); }
    void DoSomeWork2() { m_Worker.Work2(); }
    // ...
};

Now I can simply use my Wrapper class by creating an object and calling a member:

Wrapper<MyWorker> wrap;
wrap.DoSomeWork1();

But I also need to have the ability to use a “static” wrapper class (util class) besides the “local” wrapper class, to be able to make calls without creating a local object first:

StaticWrapper<MyWorker>::DoSomeWork1();

StaticWrapper<T> would be the same as Wrapper<T>, but implemented statically:

template<typename T>
class StaticWrapper
{
private:
    static T& Get()
    {
        static T worker;
        return worker;
    }

public:
    static void DoSomeWork1(); { Get().Work1(); }
    static void DoSomeWork2(); { Get().Work1(); }
    // ...
};

Question

The above solution using two different classes works, but I have to write the same code twice, once with the static keyword and once without it.

Is there any method or design pattern to avoid writing the same code twice, that is having a static and non static interface inside one class called Wrapper and then decide which one to use (static or not) during compile time when creating / using the Wrapper? Essentially something like setting the storage specifier as a template argument:

// Fantasy code
// To use a local Wrapper
Wrapper<Locally, MyWorker> localWrap;

// To use a static Wrapper
Wrapper<Statically, MyWorker>::DoSomeWork1();

Also Note: The DoSomeWork1(), DoSomeWork2(), ... methods have more in code in them than just calling a method from MyWorker. So directly using a static (or not) MyWorker is not an option.

Answer

(You can find the original answer and approach bellow)

Sergey’s answer has the most merit, and can be expanded to what you are after. Use the Wrapper itself as a static member and delegate to it. To get the syntax you are after, you simply use a “dummy policy” argument and some template specialization to structure the code.

struct MemberData;
struct StaticData;

template<typename T, typename StoragePolicy = MemberData>
class Wrapper;

template<typename T>
class Wrapper<T, MemberData>
{
private:
    T m_Worker;

public:
    void DoSomeWork1() { m_Worker.Work1(); }
    void DoSomeWork2() { m_Worker.Work2(); }
    // ...
};

template<typename T>
class Wrapper<T, StaticData>
{
private:
    static auto& get() {
        static Wrapper<T, MemberData> obj;
        return obj;
    }

public:
    static void DoSomeWork1() { get().DoSomeWork1(); }
    static void DoSomeWork2() { get().DoSomeWork2(); }
    // ...
};

// ... 

int main(int, char **)
{
    //Wrapper<MyWorker>::DoSomeWork1(); //error
    Wrapper<MyWorker> wrap;
    wrap.DoSomeWork1();

    Wrapper<MyWorker, StaticData>::DoSomeWork1();
}

All in all, this is far simpler, and avoids duplication of the implementation rather nicely. Still a bit of boilerplate to get your equivalent set of members.


You can get exactly what you want with some restructuring and policy based design. But the amount of boiler-plate may be not worth it.

We can abstract the storage of the variable (and its) associated member functions into a policy (in this case, represented as a template template-parameter, but it could be a type parameter). Then Wrapper exposes the members of the policy, and provides that implementation (as static members that accept data to operate on).

template<class Worker, template<class> class Storage>
class Wrapper;

template<class Worker>
class MemberData {
  Worker m_Worker;
protected:
  void DoSomeWork1() { Wrapper<Worker, ::MemberData>::DoSomeWork1Impl(m_Worker); }
  void DoSomeWork2() { Wrapper<Worker, ::MemberData>::DoSomeWork2Impl(m_Worker); }
};

template<class Worker>
class StaticData {
  static auto& get() {
    static Worker worker;
    return worker;
  }
protected:
  static void DoSomeWork1() { Wrapper<Worker, ::StaticData>::DoSomeWork1Impl(get()); }
  static void DoSomeWork2() { Wrapper<Worker, ::StaticData>::DoSomeWork2Impl(get()); }
};

template<class Worker, template<class> class Storage = MemberData>
class Wrapper : private Storage<Worker> {
    friend class Storage<Worker>;
    static void DoSomeWork1Impl(Worker&) { /*...*/ }
    static void DoSomeWork2Impl(Worker&) { /*...*/ }
public:
    using Storage<Worker>::DoSomeWork1;
    using Storage<Worker>::DoSomeWork2;
};

int main(int, char **)
{
    //Wrapper<int>::DoSomeWork1(); //error
    Wrapper<int> wrap;
    wrap.DoSomeWork1();
    
    Wrapper<int, StaticData>::DoSomeWork1();
}

Here it is live

As you can gauge from the complexity of this implementation for a simple example, “conditionally static” is very hard to come by. And if DoSomeWork1Impl depend on data in Wrapper, well it will become more complex still.