2

How would someone go about implementing a factory function for a templated class? Either my google searches aren't looking for the right thing, or I am misunderstanding the results. As an example:

template<typename T>
class Test
{
public:
    T data;
    void SizeOfData() { std::cout << "Data Size:" << sizeof(data) << "\n"; }
};

----this what I am trying to figure out how to do------
template <typename T>
Test<T> FactoryFunction(const std::string& type)
{
     if(type == "int")
        return Test<int>;
     if(type == "long")
        return Test<long long>;
}
----------------------------------------
int main()
{
    auto a = FactoryFunction(std::string("int"));
    auto b = FactoryFunction(std::string("long"));
    a.SizeOfData();
    b.SizeOfData();
    a.data = 1;
    b.data = 2;
}

Obviously, this code is all wrong - I am just trying to show what I want to do in theory. Can it be done? What do I look up in google - Factory functions to return templated classes? I am not even sure where to start. If someone could even point me in a direction - I really just want a function the returns the correct template instantiation based on the results of a switch or if/else list. I think conceptually the idea isn't hard, but implementing it is another thing - or I am really missing something.

Thanks for any help.

tenspd137
  • 367
  • 1
  • 12
  • 2
    You can't. How do you intend to use the created objects? Either return a `variant` or perform type erasure. – HTNW Apr 13 '22 at 05:29
  • It looks like you want to obtain runtime polymorphism from your templated function —- AFAIK the only way to achieve that would be to have your templated classes all derive from an abstract interface, and have your factory function create an object of the appropriate concrete class on the heap and return it via (smart) pointer. The calling code could then use the returned object by calling methods of the abstract interface that are implemented by the concrete subclass. – Jeremy Friesner Apr 13 '22 at 05:30

1 Answers1

3

The type T of a templated function has to be determined in compile time. Therefore you cannot do it the way you mentioned.

However - you can use the following pattern to achieve a similar result:

#include <assert.h>

class TestBase
{
public:
    virtual void SizeOfData() = 0;
};

template<typename T>
class Test : public TestBase
{
public:
    T data;
    virtual void SizeOfData() override { std::cout << "Data Size:" << sizeof(data) << "\n"; }
};

std::unique_ptr<TestBase> FactoryFunction(const std::string& type)
{
    if (type == "int")
        return std::make_unique<Test<int>>();
    if (type == "long")
        return std::make_unique<Test<long long>>();
    return nullptr;
}

int main()
{
    auto a = FactoryFunction(std::string("int"));
    assert(a);
    auto b = FactoryFunction(std::string("long"));
    assert(b);
    a->SizeOfData();
    b->SizeOfData();
    return 0;
}

Some notes:

  1. Each instance of Test (where T changes) is a differnt an unrelated class. In order to create a connection between them, I added a common base class.

  2. In order to use polymorphism, you must use refernce semantics. Therefore the factory returns a pointer (in this case a std::unique_ptr).

  3. The common method you need to invoke on all your Test objects (SizeOfData) became a virtual method in the base class.

  4. This technique is actually related to the idiom of type erasure mentioned in the comments.

UPDATE: based on the comment below, I replaced using naked news with std::make_unique. You can see more info why it is better here: Differences between std::make_unique and std::unique_ptr with new

wohlstad
  • 12,661
  • 10
  • 26
  • 39
  • Thank you for the detailed example. The one thing I forgot - is there a way for me to also access 'data' through the base class or get access to it? I would like to be able to read it back. I know that I could just put a concrete type in the base. Basically, I am working with some custom images, and I would like to look at the data, but then I would have to template the base class - which I think would break everything / can;t do for this. I suppose you could do a virtual void* data() = 0 andm implement as { return reinterpret_cast(&data) } and then cast back outside the class. –  tenspd137 Apr 13 '22 at 06:07
  • Depends what the data is. Usually for images it's enough to add a `virtual unsigned char const * GetData() = 0` in the base class (similar to what you mentioned). Indeed the base class should not be templated (otherwise we are back at the starting point). – wohlstad Apr 13 '22 at 06:16
  • BTW - for images you can also add virtual methods to the base class for retrieving common properties (like width, height, stride etc.). – wohlstad Apr 13 '22 at 06:17
  • I am just trying to think of an easy way for the user to get access to pixel data - ie so that they don't have to get a char pointer but then think about "My bits per pixel is 16, so the first pixel is two bytes, I need to cast the data to short and then get the first array value" - I am trying to make the return values "flexible", but then I still can't have some function return an appropriate reader based on a pixel type because (for example) I I wanted to overload the [] operator, it would still be T &operator[] (int i). Maybe I need to template the members and not the entire class.... –  tenspd137 Apr 13 '22 at 06:39
  • Don't think there's a way to do this like you wanted. And if you make the data access method templated, then the user will have again to know the type in the calling code. – wohlstad Apr 13 '22 at 06:57
  • Sadly - yes, I agree. I am going to have to think of some kind of "helper class" scheme or something...... Well, thanks for your help. I am gonna give you the win on this one - I appreciate the time you took to give me some ideas. –  tenspd137 Apr 13 '22 at 07:17
  • You can use `return std::make_unique>();` rather than have naked `new`s – Caleth Apr 13 '22 at 09:45
  • @Caleth you are right. It is better with `std::make_unique`. I will update my answer. – wohlstad Apr 13 '22 at 09:54