2

That's a hard one for me. I need to store different classes with same template member function in one container. The member function is always the same declaration.

struct foo_creator {
  template < class T >
  base* create() {
    return new foo<T>;
  }
};

struct bar_creator {
  template < class T >
  base* create() {
    return new bar<T>;
  }
};

The functions are applied to multiple types. At time of storage I don't know the exact types on which I want to apply the functions. That's way I can't work with a base class.

Is there a way to store them in one container or at least something that let me apply the functions later when I know the concrete type?

Because it was requested I'll mockup the real use case. I've created a container for different types of objects. To make that work the user need to introduce the types to the container. But I also want to be notified when a type is introduced, inserted, deleted and so on.

So I decided to add an observer (not knowing for which type it is called).

struct container
{
  // register an observer
  void register_observer(observer *o) { // ... }
  // introduce the type container can store
  template < class T >
  void introduce(const char *name)
  {
    T prototype;
    // observer should be called for new type
    o->notify_introduce_new_type(prototype);
  }

  template < class T >
  void insert(T *t) { // ... }
}

int main()
{
  store s;
  s.register_observer(new printer_observer);

  s.introduce<Foo>("foo") // notification
  s.introduce<Bar>("bar") // notification

  s.insert(new Foo); // notification
}

I afraid that couldn't be solvable.

Anyway thanks in advance!

zussel
  • 157
  • 9
  • 1
    Why would `create` be non-static in either case? – ildjarn Jan 23 '17 at 10:26
  • 4
    Is `create()` supposed to create a `foo*`? Or just a `foo*` as you have here? – TartanLlama Jan 23 '17 at 10:26
  • @TartanLlama Sorry, yes it creates `foo*` and `bar*`. I fixed the code. – zussel Jan 23 '17 at 10:29
  • 2
    So, you want to store a liust of functions to call at a later data? Store a `std::function`? – doctorlove Jan 23 '17 at 10:31
  • @ildjarn If it helps when `create` is static wouldn't be a problem. – zussel Jan 23 '17 at 10:34
  • I do not get question. What do you mean with `The functions are applied to multiple types. At time of storage I don't know the exact types on which I want to apply the functions. That's way I can't work with a base class.` – clickMe Jan 23 '17 at 10:35
  • 1
    @doctorlove But how can I hide the template argument then? – zussel Jan 23 '17 at 10:35
  • 2
    This will likely be difficult unless you can exhaustively enumerate `T`. Since most type erasure of this kind uses virtual functions, it won't work with templates. – TartanLlama Jan 23 '17 at 10:37
  • @SebTu I've a container for different types. That means the functions could be applied to `int`, `double` or class `FooBar` within my container. It's a kind of observer. The `create` function creates an observer for each type that is inserted into my container. Hope that helps. – zussel Jan 23 '17 at 10:40
  • 1
    Could you provide a mock-up of how using the whole system would look, in particular where types are statically known ? – Quentin Jan 23 '17 at 11:03
  • @Quentin I extended my question with a mockup. Hope that clarify things. – zussel Jan 23 '17 at 11:20
  • Not sure if [this](http://stackoverflow.com/questions/38835747/type-erasing-type-erasure-any-questions) is the answer, but definitely a good reading for your use case – W.F. Jan 23 '17 at 11:44
  • This sounds like you just need to create an interface... – Jaa-c Jan 23 '17 at 11:49
  • 1
    Not really relevant to the question, just some generic friendly advice. If possible don't return owning raw pointers. Unless you're working with an existing code base that requires you to do that, or have some other reason to do it, I would suggest using smart pointers, or at least wrapping the pointer in `gsl::owner` – notadam Jan 23 '17 at 13:41
  • Does this try to use templates comes from the fact that you have to mock it up ? Modifying the application code for test purposes is not good practice. If you have to mock this, you better compile your mock test with fake includes containing mocks with the same names. – Jean-Bernard Jansen Jan 23 '17 at 14:43
  • I came to the conclusion, that there is not a good solution for my problem. I always came to the point where I need a concrete type when it comes to either storing the class or calling its member function. So I gave up on searching for a solution and focus on another way to get what I need for my problem. Thank you all for your hints and suggestions! – zussel Jan 23 '17 at 14:48
  • You might be able to use `boost::any` or `std::any` instead of templates but I think you would have to tell each observer to expect `Foo` and `Bar` so that they can initialize the required visitor for that type. – Chris Drew Jan 23 '17 at 15:01

2 Answers2

0

You can create base class with static create function, which can accept parameters upon which you can create correct derived class.

No need to create this function in derived classes.

This designing pattern is called factory design pattern.

For your case you need to extend this logic and apply templates.

Code sample (which may be useful):

#include <cstdio>

template<typename T>
struct Base {
    static Base* create(const bool& choice) {
        if (choice)
            return new Foo<T>();
        else
            return new Bar<T>();
    }
    virtual void sayHi() = 0;
};

template<typename T>
struct Foo : public Base<T> {
    Foo(){}
    void sayHi() override {
        printf("hi from Foo\n");
    }
};

template<typename T>
struct Bar : public Base<T> {
    Bar(){}
    void sayHi() override {
        printf("hi from Bar\n");
    }
};

int main(){
    Base<int>* foo = Base<int>::create(true);
    Base<int>* bar = Base<int>::create(false);
    foo->sayHi();
    bar->sayHi();
    delete foo;
    delete bar;
    return 0;
}
  • 1
    `virtual static` ? – Drax Jan 23 '17 at 11:01
  • my bad, i meant just static [EDITED] –  Jan 23 '17 at 11:01
  • 2
    Thanks for your answer. Off course I played with over the last few days. But it ended always up with the conclusion that `virtual` and `template` doesn't fit together on member function scope. – zussel Jan 23 '17 at 11:07
  • i edited the answer (i explained wrong), derived classes do not need to have create function, just the base (factory interface) class, for more explanation and sample look at added link –  Jan 23 '17 at 11:13
  • As mentioned in my question, I need to store `Base` to call its `create` later when knowing the type. So I decided to give up on this and run for another solution. Thanks for your suggestion! – zussel Jan 23 '17 at 14:43
0

If you are willing to go with the template factory classes instead of template methods you could have it this way:

struct base_creator //abstract base factory class
{
    virtual ~base_creator() = default;
    virtual  base* create() = 0;
};

template < class T >
struct foo_creator : base_creator //creates templated foos
{
    virtual ~foo_creator() = default;
    base* create() override
    {
        return new foo<T>;
    }
};

template < class T >
struct bar_creator : base_creator //creates templated bars
{
    virtual ~bar_creator() = default;
    base* create() override
    {
        return new bar<T>;
    }
};

int main()
{
    vector<base_creator*> creators;
    foo_creator<int> fc = foo_creator<int>();
    bar_creator<string> bc = bar_creator<string>();
    creators.push_back(&fc);
    creators.push_back(&bc);
    for(base_creator * c : creators)
        c->create();
}

Here's the complete live Wandbox example.

AMA
  • 4,114
  • 18
  • 32
  • Thanks for your answer but at the time when inserting the creator, I didn't know the type. See my extended question with the mockup. – zussel Jan 23 '17 at 12:13