6

Normally, if I have a Foo, or a Bar, I would do something like:

Foo* foo = new Foo();

Bar* bar = new Bar(2,3,5);

Is there a way using templates or macros, that I can construct a function, such that I can do something like:

Foo* foo = MyAwesomeFunc(Foo);
Bar* bar = MyAwesomeFunc(Bar,2,3,5); 

The actual method signature of MyAwesomeFunc is not important to me.

Foo and Bar need not be related in any possible way, and may have completely different constructors. Additionally, I may want to support any number of classes in the future without having to actually modify the code of MyAwesomeFunc

Is this possible ? A simple way would be to have both Foo and Bar inherit from some type, say Baz, and have overloaded methods return a Baz, which you cast back to Foo or Bar...

Baz* MyAwesomeFunc(){
    return new Foo();
}

Baz* MyAwesomeFunc(int a,int b,int c){
    return new Bar(a,b,c);
}

But the problems here is you would have to write:

  1. a method for each class supported
  2. and for each kind of constructor signature.

The goal, is to write a single class, method, or macro, where we can call one function (and pass it any arguments), but call the right constructor of the passed in object. Is this possible ?

The purpose of this question is to simply explore if it is possible to do something like this in C++. Please do not bring up shared pointers, unique pointers, the pitfalls of using new, as that is off topic.

EDIT: I would like to use only the STL, and avoid using things like Boost....

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Rahul Iyer
  • 19,924
  • 21
  • 96
  • 190
  • 1
    See `boost::factory` – Ari0nhh Feb 09 '17 at 05:25
  • @Ari0nhh Thanks Ari - any way to do this easily without using Boost ? – Rahul Iyer Feb 09 '17 at 05:26
  • 3
    Pre C++11, "Perfect Forwarding" and "Variadic Templates" didnt exist. `Boost` supplied the community with the tools to make stupidly powerful code and `boost::factory` was the defacto factory which has been superceeded by perfect forwarding and variadic templates. – Gambit Feb 09 '17 at 05:33

2 Answers2

8

Since C++11 you can do it with variadic template and perfect forward. E.g. write a template function, which perfect forwards its parameters to the constructor of the object with type specified by template parameter.

template <typename T, typename... Ts>
T* MyAwesomeFunc(Ts&&... params){
    return new T(std::forward<Ts>(params)...);
}

Then use it as

Foo* foo = MyAwesomeFunc<Foo>();
Bar* bar = MyAwesomeFunc<Bar>(2,3,5); 
Community
  • 1
  • 1
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • 1
    What happens if you try to create a Bar, without all the required arguments ? – Rahul Iyer Feb 09 '17 at 05:31
  • 1
    You can use `std::is_constructible` from `type_traits`. See my answer. – Gambit Feb 09 '17 at 05:33
  • 5
    @John, Compiler error. It won't look perfect from the outside, but there are ways to fix that (concepts, and emulation thereof). – chris Feb 09 '17 at 05:33
  • 2
    @John If the arguments provided don't match the constructor of the object, then you'll get a compiler error; it's same as when you write `new Bar(something);`. – songyuanyao Feb 09 '17 at 05:35
8

Yes you can use templates and C++11's "Perfect Forwarding":

#include <type_traits>
#include <utility>

template<typename T, typename... Args>
T* createNew(Args&&... args)
{
  static_assert(std::is_constructible<T, Args...>::value, "T is not constructible with these arguments");
  return new T(std::forward<Args>(args)...);
}

Alternatively, you could check out C++11's std::make_unique and "Smart Pointers" What is a smart pointer and when should I use one?

Community
  • 1
  • 1
Gambit
  • 954
  • 9
  • 15