A class template like this
template <typename... T>
class Action {
private:
std::tuple<T...> m_args;
public:
Action(T... args) : m_args(args...) {}
}
to a class with template member. The reason why doing this is want to make only one type of class, so that object with different args still belong to the same class for easy manipulation.
class Action {
private:
// this does not work, how to declare the tuple type so that It can hold any arguments list.
template <typename... T>
std::tuple<T...> m_args;
public:
template <typename... T>
Action(T... args) : m_args(args...) {}
}
Put your mind in the perspective of the compiler. How would our trusty friend know how much storage is required for Action, if that size would be dependent upon what constructor was resolved? This is not possible.
template<typename... T>
Action(T... args) : m_args(args...) {}
// wait what???
Action(int someArg) : m_args(someArg) {}
Let's say the second constructor was valid, or we had two Action objects with different arguments passed into the constructor -- what should be sizeof(Action)?
If you get stuck on an issue like this, think as the compiler: there was probably some person who had to give a good enough reason why it shouldn't be supported, simply because it only complicates the implementation and also has performance implications.
Since you mentioned using a pointer instead of an aggregated object, I figured I'd show you how to do this for completeness. Do note that this has performance implications, because you are now allocating the memory for m_args on the heap, rather than the stack.
class Action
{
public:
template<typename... T>
using TArgs = std::tuple<T...>;
std::shared_ptr<void> m_args;
template<typename... T>
Action(T... args)
: m_args(std::make_shared<TArgs<T...>>(args...)) {}
};
Action myAction(1, 2.0, 3.5f);
auto *myActionArgs =
static_cast<Action::TArgs<int, double, float>*>(myAction.m_args.get());
// 2.0
double secondArg = std::get<1>(*myActionArgs);
This does not look like fun to maintain.
Related
I would like to define a class which inherits from a bunch of classes but which does not hide some specific methods from those classes.
Imagine the following code:
template<typename... Bases>
class SomeClass : public Bases...
{
public:
using Bases::DoSomething...;
void DoSomething(){
//this is just another overload
}
};
The problem is now if just one class does not have a member with the name DoSomething I get an error.
What I already tried was emulating an "ignore-if-not-defined-using" with a macro and SFINAE but to handle all cases this becomes very big and ugly!
Do you have any idea to solve this?
It would be really nice if I could define: "Hey using - ignore missing members".
Here I have some sample code: Godbolt
The problem with Jarod42's approach is that you change what overload resolution looks like - once you make everything a template, then everything is an exact match and you can no longer differentiate between multiple viable candidates:
struct A { void DoSomething(int); };
struct B { void DoSomething(double); };
SomeClass<A, B>().DoSomething(42); // error ambiguous
The only way to preserve overload resolution is to use inheritance.
The key there is to finish what ecatmur started. But what does HasDoSomething look like? The approach in the link only works if there is a single, non-overloaded, non-template. But we can do better. We can use the same mechanism to detect if DoSomething exists that is the one that requires the using to begin with: names from different scopes don't overload.
So, we introduce a new base class which has a DoSomething that will never be for real chosen - and we do that by making our own explicit tag type that we're the only ones that will ever construct. For lack of a better name, I'll name it after my dog, who is a Westie:
struct westie_tag { explicit westie_tag() = default; };
inline constexpr westie_tag westie{};
template <typename T> struct Fallback { void DoSomething(westie_tag, ...); };
And make it variadic for good measure, just to make it least. But doesn't really matter. Now, if we introduce a new type, like:
template <typename T> struct Hybrid : Fallback<T>, T { };
Then we can invoke DoSomething() on the hybrid precisely when T does not have a DoSomething overload - of any kind. That's:
template <typename T, typename=void>
struct HasDoSomething : std::true_type { };
template <typename T>
struct HasDoSomething<T, std::void_t<decltype(std::declval<Hybrid<T>>().DoSomething(westie))>>
: std::false_type
{ };
Note that usually in these traits, the primary is false and the specialization is true - that's reversed here. The key difference between this answer and ecatmur's is that the fallback's overload must still be invocable somehow - and use that ability to check it - it's just that it's not going to be actually invocable for any type the user will actually use.
Checking this way allows us to correctly detect that:
struct C {
void DoSomething(int);
void DoSomething(int, int);
};
does indeed satisfy HasDoSomething.
And then we use the same method that ecatmur showed:
template <typename T>
using pick_base = std::conditional_t<
HasDoSomething<T>::value,
T,
Fallback<T>>;
template<typename... Bases>
class SomeClass : public Fallback<Bases>..., public Bases...
{
public:
using pick_base<Bases>::DoSomething...;
void DoSomething();
};
And this works regardless of what all the Bases's DoSomething overloads look like, and correctly performs overload resolution in the first case I mentioned.
Demo
How about conditionally using a fallback?
Create non-callable implementations of each method:
template<class>
struct Fallback {
template<class..., class> void DoSomething();
};
Inherit from Fallback once for each base class:
class SomeClass : private Fallback<Bases>..., public Bases...
Then pull in each method conditionally either from the base class or its respective fallback:
using std::conditional_t<HasDoSomething<Bases>::value, Bases, Fallback<Bases>>::DoSomething...;
Example.
You might add wrapper which handles basic cases by forwarding instead of using:
template <typename T>
struct Wrapper : T
{
template <typename ... Ts, typename Base = T>
auto DoSomething(Ts&&... args) const
-> decltype(Base::DoSomething(std::forward<Ts>(args)...))
{
return Base::DoSomething(std::forward<Ts>(args)...);
}
template <typename ... Ts, typename Base = T>
auto DoSomething(Ts&&... args)
-> decltype(Base::DoSomething(std::forward<Ts>(args)...))
{
return Base::DoSomething(std::forward<Ts>(args)...);
}
// You might fix missing noexcept specification
// You might add missing combination volatile/reference/C-elipsis version.
// And also special template versions with non deducible template parameter...
};
template <typename... Bases>
class SomeClass : public Wrapper<Bases>...
{
public:
using Wrapper<Bases>::DoSomething...; // All wrappers have those methods,
// even if SFINAEd
void DoSomething(){ /*..*/ }
};
Demo
As Barry noted, there are other drawbacks as overload resolution has changed, making some call ambiguous...
Note: I proposed that solution as I didn't know how to create a correct traits to detect DoSomething presence in all cases (overloads are mainly the problem).
Barry solved that, so you have better alternative.
You can implement this without extra base classes so long as you’re willing to use an alias template to name your class. The trick is to separate the template arguments into two packs based on a predicate:
#include<type_traits>
template<class,class> struct cons; // not defined
template<class ...TT> struct pack; // not defined
namespace detail {
template<template<class> class,class,class,class>
struct sift;
template<template<class> class P,class ...TT,class ...FF>
struct sift<P,pack<>,pack<TT...>,pack<FF...>>
{using type=cons<pack<TT...>,pack<FF...>>;};
template<template<class> class P,class I,class ...II,
class ...TT,class ...FF>
struct sift<P,pack<I,II...>,pack<TT...>,pack<FF...>> :
sift<P,pack<II...>,
std::conditional_t<P<I>::value,pack<TT...,I>,pack<TT...>>,
std::conditional_t<P<I>::value,pack<FF...>,pack<FF...,I>>> {};
template<class,class=void> struct has_something : std::false_type {};
template<class T>
struct has_something<T,decltype(void(&T::DoSomething))> :
std::true_type {};
}
template<template<class> class P,class ...TT>
using sift_t=typename detail::sift<P,pack<TT...>,pack<>,pack<>>::type;
Then decompose the result and inherit from the individual classes:
template<class> struct C;
template<class ...MM,class ...OO> // have Method, Others
struct C<cons<pack<MM...>,pack<OO...>>> : MM...,OO... {
using MM::DoSomething...;
void DoSomething();
};
template<class T> using has_something=detail::has_something<T>;
template<class ...TT> using C_for=C<sift_t<has_something,TT...>>;
Note that the has_something here supports only non-overloaded methods (per base class) for simplicity; see Barry’s answer for the generalization of that.
I'm sure there's a name for what I'm looking for, I just don't know it (and if I did, I'd probably find the answer already). Basically, I want to implement my own lightweight version of std::function for sports. I want to initialize it with a lambda, and later invoke it. I can wrap the lambda with my template wrapper class, nothing to it:
struct CInvokableAbstract {
virtual ~CInvokableAbstract() = default;
};
template <class InvokableObject>
struct CInvokableBasic : public CInvokableAbstract
{
CInvokableBasic(InvokableObject&& target) : _invokable(std::move(target)) {}
template <typename... Args>
typename std::result_of<decltype(&InvokableObject::operator())(Args...)>::type operator()(Args... args) const
{
return _invokable(std::forward<Args>(args)...);
}
private:
InvokableObject _invokable;
};
Now I can make my class that's semantically similar to std::function, but how can I store the exact type of the lambda in order to convert the type-erased object back to its original concrete type?
struct CInvokableDeferred
{
template <class InvokableObject>
CInvokableDeferred(InvokableObject&& target) noexcept : _invokable(std::make_unique<CInvokableBasic<InvokableObject>>(std::move(target))) {}
template <typename... Args>
void operator()(Args... args) const
{
// How can I know the original concrete type to cast this to?
static_cast<???>(_invokable.get())->(std::forward<Args>(args)...);
}
private:
std::unique_ptr<CInvokableAbstract> _invokable;
};
I can't think of any template trickery that could do that, yet we know it's possible (unless std::function uses some compiler built-ins, or otherwise is implemented internally in the compiler rather than being normal C++ code).
Note that I'm using a compiler that doesn't have full C++17 support, so I can't use e. g. auto-deduced return type.
You need to rewrite your base class as follows:
template <typename Ret, typename... Args>
class CInvokableAbstract {
virtual ~CInvokableAbstract() = default;
virtual Ret operator()(Args... args) = 0;
};
This will make your base class dependent on the signature (which it has to be in order to be usable) and provide the actual interface for the invocable object.
Note that this part of code actually has nothing to do with type-erase, it's just plain old dynamic polymorphism. It's the combination of static (CInvokableBasic template) and dynamic (CInvokableAbstract interface) polymorphisms that make type-erasure possible.
I have a nice and small jack-of-all trades Array type that fits all my needs until now.
template <typename T>
class Array
{
...
public:
int Data; // custom value
virtual void InitData() { Data = 0; }
Array(const Array& array);
template <typename U, typename = std::enable_if<std::is_same<U, T>::value, U>> Array(const Array<U>& array);
template <typename... Ts> Array(const Ts&... items);
void Add(const T& item);
template <typename... Ts> void Add(const T& item, const Ts&... rest);
void Add(const Array& array);
}
The template <typename... Ts> Array(const Ts&... items); lets me do Array<T> array = { ... }, and to assign and return {...} initializer lists. Because none of the constructors are explicit, it is incredibly convenient, but is also the reason I'm stuck now.
I would like to be able to add anything "reasonable" to the arrays. My main use case right now is:
using Curve = Array<float2>;
class Poly : public Array<float2> { using Array::Array; void InitData() override { Data = 1; } };
class Curve2 : public Array<float2> { using Array::Array; void InitData() override { Data = 2; } };
class Curve3 : public Array<float2> { using Array::Array; void InitData() override { Data = 3; } };
The std::is_same<> stuff above is specifically to be able to treat all the curves as the same but not the same: the curve types of different degree, and everything is nicely "statically typed", so all I do in a function like DrawCurve(const Curve&) is check the degree and then take appropriate action. Curve is a nice alias for Array, and Curve2 etc. are degree specializations. It works very nicely.
When I get into curve construction, I usually have a curve object, to which I add either points or curve segments. So I'd like to be able to do:
Curve3 curve;
curve.Add(float2()); // ambiguity
curve.Add(Array<float2>());
Unfortunately, I get an ambiguity here when I call add, because Add() will take either a float2 or an Array<float2>, which works fine, but an Array has the implicit constructor template <typename... Ts> Array(const Ts&...), which can take float2 as an argument. So the ambiguity is between
Array::Add(float2()); // and
Array::Add(Array<float2>(float2()));
I have tried making constructors that take arrays explicit, like
template <typename A, typename = std::enable_if<std::is_same<A, Array>::value, A>>
void Add(const Array& array);
But then I get new conversion errors from Curve3 to float2 etc. and it becomes a mess.
My hope is that somewhere in the depths of templates or other C++ goodies lies a simple solution that is just what I need. (Yes, I know that I can just rename the methods ::AddItem() and ::AddArray() and the problem will be over in a second, but I don't want this because eventually I want to double all this with += and then mostly just use that.
Any ideas?
Observe that you want
template <typename... Ts> Array(const Ts&... items);
to be used only if the parameter pack contains at least one item, and that item's type is not an Array template instance. If the parameter pack is empty, this becomes a default constructor, so let's handle this case separately. Go ahead and explicitly define a default constructor, if you need to, and have it do what it needs to do; now we can eliminate that possibility from this use case, and forge ahead.
Having gotten that out of the way, what you want to do here is to use this constructor only when it has one argument:
template <typename T1, typename... Ts> Array(const T1 &t1, const Ts&... items);
You will have to modify this constructor to use the explicit t1, in addition to the existing parameter pack it uses. That should be simple enough, but that won't be sufficient. There's still ambiguity. You want to have this constructor selected only if T1 is not an Array.
There's probably a way to come up with something convoluted and stuff it into a single std::enable_if, and shove it into this template. But for clarity, and simplicity, I would use a helper class:
template<typename T> class is_not_array : public std::true_type {};
template<typename T>
class is_not_array<Array<T>> : public std::false_type {};
And then add a simple std::enable_if into this constructor's template to use SFINAE to select this constructor only when its first template parameter is not an Array, similar to how you're using std::enable_if already, in the other constructor.
This should resolve all ambiguities. This constructor should then be picked only with at least one template parameter that's not an Array, with the assistance of this helper class. When it's an Array, this constructor will not be resolvable, and that case will go to the other constructor.
I would also suggest using universal references in the templates, instead of const T &s.
Unlike function declarations with parameter packs, I've found that classes require the type for each argument in the angle brackets...
Component<IntegerPair, int, int> temp(40, 5);
...which seems redundant. Here's how I defined Component:
template<typename T, class... T_Args>
class Component
{
public:
Component(T_Args... args)
: m_data(args...)
{}
T m_data;
};
Is there a way to remove int, int from the above statement?
If so, is it ok to remove it?
Also, is my way of instantiation m_data safe? When using
std::forward<T_Args>(args)... my compiler told me I didn't have a
constructor that could convert all of the argument types.
One way is to make the constructor a template:
#include <utility>
struct IntegerPair {
IntegerPair(int, int) {}
};
template<typename T>
class Component
{
public:
template<typename... T_Args>
Component(T_Args&&... args)
: m_data(std::forward<T_Args>(args)...)
{}
T m_data;
};
int main()
{
Component<IntegerPair> c {1,2};
}
This is functionally equivalent to std::vector and its member function emplace_back. It's perfectly ok, IMO. The error messages are pretty cryptic, as usual in template constructs like this, but this can be mitigated with an appropriate static_assert.
template parameter deduction only work for function calls so the basic pattern to achieve what you want looks like this:
template<typename T, class... T_Args>
Component<T, T_Args...> makeComponent(T_Args&&... args) {
return Component<T, T_Args...>(std::forward<T_Args>(args)...);
}
Usage:
auto c = makeComponent<IntegerPair>(1, 1)
Basically I just want to wrap any callable object and their arguments in a Task obj that can be called later. Here are the code that come to mind:
Lets say all those callable type have a member type can be seemed as if they are defined like this:
template<typename TReturn, typename...TArgs>
struct SomeFunction{
using ArgTypes = TArgs; // won't compile of course
}
And the Task Template can be defined like this:
template<typename TFunction>
class Task {
public:
Task(TFunction fun, typename TFunction::ArgTypes...args) // won't compile
: fun_(fun), args_(args){}
void operator()()
{
fun_(args_...); // won't compile: workaround 1
}
private:
typename TFunction::ArgTypes... args_; // won't compile: workaround 2
TFunction fun_;
};
The problem lies in the definition of Task's constructor. Is there any way to achieve it? Of course I can define it as a template constructor:
template<typename...TArgs>
Task(TFunction fun, TArgs...args)
But in this way the compiler won't know that the TArgs are the same as TFunction::ArgTypes. Consequently, the error messages are ridiculous when wrong arguments passed to it.
workaround 1 : C++ How to store a parameter pack as a variable
workaround 2 : Is it possible to "store" a template parameter pack without expanding it?
You could use a std::tuple<TArgs...> to store the arguments and unpack them in the call-operator. If you want to define TArgs in some way in a function type, you should define them as a tuple there:
template<typename TReturn, typename...TArgs>
struct SomeFunction{
using ArgTypesTuple = std::tuple<TArgs...>;
// ^^^^^^^^^^
}
But nevertheless, I think it's not worth the effort to save the arguments in your Task object with a lot of boilerplate code... The call operator would look somewhat ugly to reconstruct / unpack the arguments from the tuple to a list of arguments.
The much easier solution is to make a lambda which captures the arguments at construction time of your Task object, which doesn't even need to be a template anymore:
class Task {
public:
template<typename TFunction, typename ...ArgTypes>
Task(TFunction fun, ArgTypes... args)
: fun_([=]{ fun(args...); }) {}
void operator()()
{
fun_();
}
private:
std::function<void()> fun_;
};
What you may do with some changes:
template <typename TReturn, typename...TArgs>
struct SomeFunction{
using ReturnType = TReturn;
using ArgTypes = std::tuple<TArgs...>;
};
For Task:
template <typename TFunction, typename TupleArg = typename TFunction::ArgTypes>
class Task;
template <typename TFunction, typename... TArgs>
class Task<TFunction, std::tuple<TArgs...>>
{
public:
Task(TFunction fun, TArgs...args) : fun_(fun), args_(args...) {}
void operator()()
{
call(make_index_sequence<sizeof...(TArgs)>{});
}
private:
template <std::size_t ... Is>
void call(index_sequence<Is...>)
{
fun_(std::get<Is>(args_)...);
}
private:
TFunction fun_;
std::tuple<TArgs...> args_;
};
Live example