Get class' constructor arguments - c++

Is there any way to get the parameters of a constructor in C++?
template <typename T>
class Test {
public:
// Get the constructor arguments from class T
Test(constructor<T>());
private:
T* value_;
};
template <typename T>
Test(constructor<T>()) {
value_ = new T(constructor);
}
int main() {
// std::string can be initialized by string literal
Test<std::string> test("Text");
return 0;
}
I know I can just use T as the argument but I don't want to pass the object itself, just the parameters it takes.
Anyway of doing this in standard C++?

I don't know what "vanilla" C++ is, but what you can do is accept any arguments which the other class allows, and forward them:
template <typename T>
class Test {
public:
template <class... Args,
std::enable_if_t<std::is_constructible<T, Args&&...>::value, int> = 0>
Test(Args&&... args)
: value_(std::forward<Args>(args)...)
{ }
Test(T&& val)
: value_(std::move(val))
{ }
private:
T value_;
};
The second constructor there is to allow passing a brace-init-list into the Test constructor. This still isn't quite a perfect stand-in, but it's pretty good.

Related

How to efficiently initialize a std::variant data member in a class template

Consider the following class template, that can hold either a value of type T or an instance of some ErrorInfo class, using a std::variant data member:
template <typename T>
class ValueOrError
{
private:
std::variant<T, ErrorInfo> m_var;
};
How can I efficiently initialize the variant T alternative?
I can initialize it with a constructor like this:
template <typename T>
class ValueOrError
{
public:
explicit ValueOrError(const T& val)
: m_var{val}
{
}
…
};
But what syntax/coding technique can I use to enable move semantics optimization during initialization?
If I define a constructor taking a T&&, should I std::move or std::forward the parameter into the m_var?
template <typename T>
class ValueOrError
{
public:
// Efficient initialization with move semantics
explicit ValueOrError(T&& val)
: m_var{ /* ?? */ }
{
}
…
};
Note on interactions with ErrorInfo constructor overload
The ValueOrError template should also have a constructor overload that takes an ErrorInfo and initializes the variant member accordingly:
template <typename T>
class ValueOrError
{
public:
// Initialize with error code instead of T
explicit ValueOrError(const ErrorInfo& error)
: m_var{error}
{
}
…
};
It’s important that the generic T constructor overload interacts properly with the specific ErrorInfo overload.
ErrorInfo is a tiny class that wraps an error code (e.g. a simple integer), and can be constructed from such error code:
class ErrorInfo
{
public:
explicit ErrorInfo(int errorCode)
: m_errorCode{errorCode}
{
}
int ErrorCode() const
{
return m_errorCode;
}
// … other convenient methods
// (e.g. get an error message, etc.)
private:
int m_errorCode;
};
A C++20 version using perfect forwarding:
#include <concepts> // std::constructible_from
template <class T>
class ValueOrError {
public:
explicit ValueOrError(const ErrorInfo& error) : m_var{error} {}
template<class... Args>
requires std::constructible_from<T, Args...>
explicit ValueOrError(Args&&... val) :
m_var(std::in_place_type<T>, std::forward<Args>(val)...)
{}
private:
std::variant<T, ErrorInfo> m_var;
};
A C++17 version, also using perfect forwarding, could look like this:
#include <type_traits> // std::is_constructible_v, std::enable_if_t
template <class T>
class ValueOrError {
public:
explicit ValueOrError(const ErrorInfo& error) : m_var{error} {}
template<class... Args,
std::enable_if_t<std::is_constructible_v<T, Args...>, int> = 0>
explicit ValueOrError(Args&&... val)
: m_var(std::in_place_type<T>, std::forward<Args>(val)...) {}
private:
std::variant<T, ErrorInfo> m_var;
};
Example usages:
class foo { // A non default constructible needing 3 constructor args
public:
foo(double X, double Y, double Z) : x(X), y(Y), z(Z) {}
private:
double x, y, z;
};
int main() {
ValueOrError<foo> voe1(1., 2., 3.); // supply all three arguments
// use the string constructor taking a `const char*`:
ValueOrError<std::string> voe2("Hello");
std::string y = "world";
// use the string constructor taking two iterators:
ValueOrError<std::string> voe3(y.begin(), y.end());
}
I would do this this way in C++17 (using "perfect forwarding" + SFINAE):
template <typename T>
class ValueOrError
{
public:
template<typename U>
explicit ValueOrError(U&& val, std::enable_if_t<std::is_constructible_v<T, U>>* = nullptr)
{
m_var.template emplace<T>(std::forward<U>(val));
}
private:
std::variant<T, ErrorInfo> m_var = ErrorInfo{0};
};
Question is how this interact with constructors were error should be used?
Or initialization list version:
template <typename T>
class ValueOrError {
public:
template <typename U>
explicit ValueOrError(U&& val, std::enable_if_t<std::is_constructible_v<T, U>>* = nullptr)
: m_var { std::in_place_type<T>, std::forward<U>(val) }
{
}
private:
std::variant<T, ErrorInfo> m_var;
};
I have doubts if version with multiple arguments to construct T should be implemented. It is possible, but IMO will make code harder to read.
https://godbolt.org/z/scxacMn3W

Template arguments in constructor of non-template class

I want to have a constructor of a non-template class which is templated by a type. Can anyone help here?
class A
{
public:
static int GetId(){ return 5;}
};
class B
{
public:
B(int id){ _id = id;}
template<typename T>
B() {_id = T::GetId();}
template<typename T>
static B* newB() {return new B(T::GetId());}
private:
int _id;
};
void doSome()
{
B* p1 = B::newB<A>(); //works
B* p2 = new B<A>(); //doesn't compile -- ">>B<< is no template"
}
All template parameters of a constructor template must be deducible (or have default arguments), because there is no syntax for explicitly passing template arguments to a constructor (as you've learned).
There are several possible ways around this:
Provide a constructor-like function template. You're already doing this with newB, there's just no need to force dynamic allocation:
template <class T>
B create() { return B(T::GetId()); }
Provide a tag type and parameterise the consturctor by that:
template <class T>
struct Tag {};
class B
{
public:
template <class T>
B(Tag<T>) : _id(T::GetId()) {}
};
//usage:
B b(Tag<A>());
You cannot explicitly specify the constructor template parameter. It must be deductible.
One solution is to use a helper parameter:
template <class T>
struct Type_holder { using Type = T; };
class B {
public:
B(int id) : id{id} {}
template<typename T>
B(Type_holder<T>) : id{T::GetId()} {}
private:
int id;
};
auto foo()
{
B b{Type_holder<A>{}};
}
Also, please use constructor initialization lists. And careful with those dynamic allocations. Don't use it if it's not needed. And when it's needed use smart pointers.

Variadic template type unpacking into map keys

I'm trying to create a class which will contain a map of type_index keys mapped to pointers of each type passed as a template argument. This would allow me to specify a series of types my class will rely on in it's declaration.
I've done a bit of research but can only seem to find ways to unpack arguments, rather than types. I'm new to this subject, and would appreciate any clarification on terminology, or references to relevant text.
template <typename T>
T* SomeFakeFactoryGetter() { return new T(); }
template <class... Injected>
class UtilityProvider
{
public:
template <class U>
U* GetUtility()
{
std::type_index idx = std::type_index(typeid(U));
assert(_injectedClasses.find(idx) != _injectedClasses.end());
return reinterpret_cast<U*>(_injectedClasses[idx]);
}
// **
// How would I *unpack* all types for use as indices into my map?
// ( I realise this function is not what I want.)
template <Injected... C>
void Unpack()
{
_injectedClasses[std::type_index(typeid(C))] = SomeFakeFactoryGetter<C>();
}
private:
typedef std::unordered_map<std::type_index, void*> InjectedMap;
InjectedMap _injectedClasses;
};
class Bar{ public: void A() { printf("Hello bar"); } };
class Baz{ public: void B() { printf("Hello baz"); } };
class Nope {};
class Foo : public UtilityProvider<Bar, Baz>
{
public:
Foo()
{
GetUtility<Bar>()->A();
GetUtility<Nope>(); // Fail. User must specify which utilities this class will use.
}
};
What I've done in this situation is to create a dummy function to expand these expressions into, but it looks quite hideous:
template <int ... Dummies>
void dummy(int&& ...){}
template <class ... C>
void Unpack()
{
dummy(((_injectedClasses[std::type_index(typeid(C))] =
SomeFakeFactoryGetter<C>()), 0)...);
}
Note that in your case I think you'll be better off with using insert with an initializer_list:
template <class ... C>
void Unpack()
{
_injectedClasses.insert({std::make_pair(std::type_index(typeid(C)),
SomeFakeFactoryGetter<C>())...});
}
I couldn't find a direct mention of this but I believe there is an important difference between the two methods, in case you didn't already know. insert will not override existing key-value pairs, whereas operator[] will. This can affect which method you should use if if this is important to you.
An alternative approach:
template <typename ... C> struct Unpacker;
template <typename Tail, typename ... Queue>
struct Unpacker<Tail, Queue...>
{
void operator () (InjectedMap& injectedClasses) const
{
_injectedClasses[std::type_index(typeid(Tail))] = SomeFakeFactoryGetter<Tail>();
Unpacker<Queue...>()(injectedClasses);
}
};
template <>
struct Unpacker<>
{
void operator () (InjectedMap& injectedClasses) const {}
};

Designing template class with generic private member

I suppose some other folks ran into this design issue before so I hope someone could give me some advice on what to do: I have a class that is supposed to hold a private generic object. As far as I can tell, I can't get away without making the entire class a template. FINE. But now, is there any way to infer the type of the underlying object during construction from the constructor parameter, without explicitly specifying it (I want to omit the template parameter, Derived, when I instantiate the Test class):
#include <iostream>
template <typename T>
class Generic
{
};
class Derived : public Generic<int>
{
public:
Derived ();
int GetFoo ();
private:
int m_foo;
};
template <typename T>
class Test
{
public:
Test (T &underlying);
private:
T m_underlying;
};
Derived::Derived ()
{
this->m_foo = 666;
}
int Derived::GetFoo ()
{
return this->m_foo;
}
template<typename T>
Test<T>::Test (T &underlying) : m_underlying(underlying)
{
std::cout << this->m_underlying.GetFoo() << std::endl;
}
int main ()
{
Derived underlying;
Test<Derived> test(underlying);
return 0;
}
Is there any other design strategy that I should be aware of, in order to achieve my goal?
Usually you have a class template together with a type-deducing function template:
template <typename T>
struct Foo
{
Foo(T const &);
};
template <typename T>
Foo<T> make_foo(T const & t)
{
return Foo<T>(t);
}
Usage:
auto foo = make_foo(1728); // makes a Foo<int>
This idea is used countless times in the standard library (such as make_pair, make_tuple, make_shared). The guiding principle is that you should say the desired typename at most one time, and not at all if it can be inferred.
Just make a function to create Test object:
template <typename T>
Test<T> make_test(T& underlying)
{
return Test<T>(underlying);
}
int main ()
{
Derived underlying;
auto test = make_test(underlying);
return 0;
}

How to have std::function or lambda as (optional) template parameter?

Hi I was playing around with TMP and was thinking of generating of a class
that looks something like:
template<typename T, typename LogFunc>
class
{
(where LogFunc should be defaulted to "nop" function)
Idea is to have a class that defines some functionality for instances of type T, for example checks if the number is even, and also has the option to log by calling
void memberFunc(T& t)
{
LogFunc(t);
}
or maybe
void memberFunc(T& t)
{
LogFunc lf;
lf(t);
}
Can it be done?
From reading A on SO, lambdas are kind of problematic as templ params.
BTW if somebody cares this is what I tried but it prints out
:(
The problem is that the type of a lambda is a compiler-enforced singleton; it has only one value, which is the lambda itself; furthermore, the type has a deleted constructor. So you can't pass lambdas as part of a template instantiation, even with decltype. But there's nothing stopping you from passing them as constructor arguments.
However, here we run into another problem: constructor arguments are not used to deduce a template instantiation (which is why the standard library provides utilities like make_pair and make_tuple). So we need a templatized factory function.
With all that, the solution is pretty simple:
template<typename T, typename LogFunc>
class Foo {
public:
Foo(const T& t, LogFunc fn) : t_(t), lfn_(fn) {}
//...
private:
T t_;
LogFunc lfn_;
};
struct Noop {
template<typename...A>
void operator()(A...) { };
};
template<typename T, typename LogFunc=Noop>
Foo<T, LogFunc> make_foo(const T& t, LogFunc func=LogFunc()) {
return Foo<T, LogFunc>(t, func);
}
This will not answer directly, but gives a number of hints about what you did.
The LogFunc parameter is a type (not an object), hence
LogFunc(t) creates a temporary LogFunc giving t as parameter (you are in fact calling the LogFunc::LogFunc(T&) contructor).
LogFunc lf; lf(t); creates a stack-living default contructed Logfunc, named lf, and lf(t) calls its LogFunc::operator()(T&) member function.
LogFunc()(t) creates a temporary default-constructed LogFUnc and calls operator()(T&) on it.
About lambdas, they are in fact classes whose constructor takes the captured varaibles, and whose operator() takes the parameters you declare. But they exist only "internaly" to the compiler, and don't have a "name" you can refer to.
What you can do is deduce its type with a decltype, or with a free-function.
Typically a parametric functional class stores a frunction object, initialized at construction.
#include <iostream>
template<class Fn>
class LogFunc
{
public:
LogFunc(Fn f) :fn(f) {}
template<class T>
void memberFunc(T& t)
{ fn(t); }
private:
Fn fn;
};
template<class Fn>
LogFunc<Fn> makeLogFunc(Fn f)
{ return LogFunc<Fn>(f); }
int main()
{
int x=5;
auto lf = makeLogFunc([](int& a){ std::cout << a << std::endl; });
lf.memberFunc(x);
return 0;
}
compile as "g++ -pedantic -Wall -std=c++11", and will ouptut
5
The other answers are all fine, but you can also just pass in a constructor argument with a std::function<T>. That looks like this:
#include <functional>
#include <iostream>
template <typename T> void someOther(T val){
std::cout << "used other "<<val<<std::endl;
}
template <typename T> void noop(T val){
std::cout << "noop "<<val<<std::endl;
}
template<typename T>
struct A{
A(std::function<void(T)> f =noop<T> ) : mf(f){}
void memberFunc(T valx){
mf(valx);
}
std::function<void(T)> mf;
};
int main(){
A<int> aNoop; ;
A<float> aSomeOther{someOther<float>} ;
aNoop.memberFunc(5);
aSomeOther.memberFunc(3.55);
}
An alternative is to use functor classes, like this:
#include <iostream>
template <typename T> struct OtherC{
void operator()(T v){ std::cout <<"other "<<v<<std::endl; };
};
template <typename T> struct NoopC{
void operator()(T){ std::cout << "noop"<<std::endl; };
};
template<typename T, template <typename X> class F = NoopC >
struct A{
static void memberFunc(T valx){ F<T>()(valx); }
};
int main(){
A<int> aNoop;
A<float,OtherC> aSomeOther ;
aNoop.memberFunc(5);
aSomeOther.memberFunc(3.55);
}