I have this template class:
template<typename T, T F(const std::string&)>
struct Builder
{
T operator()(const std::string& s) const { return F(s); }
typedef T type;
};
since I need a class holding a function and the value returned by the function (since I need a reference to it).
As you can see I have two template parameters, but actually the first is redundant. Is is possible to remove it in some way? Template alias? Something better than a macro
Return type T of the second template parameter must be defined before its first usage anyway (as the C++ rule states that any entity can be used only after its declaration), and the only way to declare is to use preceding type template parameter (as you did), so you cannot omit the first parameter.
Related
I've got the following code:
template <bool condition>
struct enable_if { };
template <>
struct enable_if<true> { using type = bool; };
template <typename T>
class is_callable {
using Yes = char[1];
using No = char[2];
template <typename U> static Yes& filter(decltype(&U::operator()));
template <typename U> static No& filter(...);
public:
constexpr operator bool() { return sizeof(filter<T>(nullptr)) == sizeof(Yes); }
};
template <typename Lambda, typename enable_if<is_callable<Lambda>{}>::type = true>
void doSomethingWithLambda(Lambda func) {
func();
}
int main() {
doSomethingWithLambda([]() { });
}
The important part is the enable_if<is_callable<Lambda>{}>::type part.
One is forced to instantiate is_callable<Lambda> with {} because if one were to use (), C++ would mistake it for a function call.
Feel free to correct me if I'm wrong, but as far as I know, C++ assumes it is a function in the () case so that the type of expression isn't determined after the time of writing, saving everyone a headache. What I mean by that is, assuming you had a function version and a class version of is_callable (separated by SFINAE using enable_if or something along those lines), the type Lambda could determine the true meaning of (), either a function call or an instantiation. Like I said, as far as I know, C++ wants to avoid this confusion, so it assumes function call and fails if such a function does not exist.
Based on the assumptions above, the following shouldn't work:
enable_if<(bool)is_callable<Lambda>()>::type
What does it matter if I cast the result of the function call (never mind that functions couldn't even be evaluated in this context)? Why is this suddenly treated as an instantiation instead of a function call?
No, your understanding is not correct.
Firstly, a name can't refer to both a class template and a function template. If that happens the program is ill-formed. (And defining both in the same scope is not allowed to begin with.)
Secondly, is_callable<Lambda>() as template argument is not a function call to begin with. It is a function type. It is the type of a function which has no parameters and returns a is_callable<Lambda>.
When the compiler parses a template argument, it can interpret it in two ways: Either as a type or as an expression (or as a braced-init-list), because template parameters can be type parameters or non-type parameters.
When the compiler reads is_callable<Lambda>() it notices that is_callable is a class template and then realizes that is_callable<Lambda> is therefore a type. If you have a type, let's shorten it to T, then T() can either be syntax representing the type of a function returning T and taking no arguments, or it can be an expression formed from one single functional notation explicit cast (which you imprecisely call "instantiation").
There is no way to differentiate these two cases in the context, but the compiler needs to know whether this is a type template argument or a non-type template argument. So there is a rule saying that such ambiguities are always resolved in favor of a type.
If is_callable was a function template instead, there would be no ambiguity, because then is_callable<Lambda> is not a type and therefore is_callable<Lambda>() cannot be a function type. It must be a function call instead and therefore an expression and non-type template argument.
When you write (bool)is_callable<Lambda>() this is not valid syntax for a type and therefore there is no ambiguity. It is a non-type template argument and an expression. And is_callable<Lambda>() is a funcational notation explicit cast because is_callable<Lambbda> is a type. If is_callable was a function template instead of a class template, then it would be a function call.
template<typename Container, typename Ret, typename ...Args>
struct BindImpl {
template<Ret (Container::*MemberFunc)(Args...)>
class Callable {
public:
inline constexpr Callable (Container *container) :
m_container(container)
{}
inline Ret operator() (Args ...args) const
{
return (m_container->*MemberFunc)(std::forward<Args>(args)...);
}
inline Function<Ret(Args...)> toFunction() const
{
return Function<Ret(Args...)>(*this);
}
private:
Container *m_container;
};
};
template<typename Container, typename Ret, typename ...Args>
BindImpl<Container, Ret, Args...> DeduceImpl (Ret (Container::*)(Args...));
This code is called like this:
(typename decltype(::AIpStack::BindPrivate::DeduceImpl(&EthIpIface::driverSendIp4Packet)) ::template Callable<&EthIpIface::driverSendIp4Packet>((this)).toFunction())
I'm trying to understand what this code does. It apprently is a way to bind function pointers (like &EthIpIface::driverSendIp4Packet) to something.
The line above is from this macro, which fills this struct member, if anyone is intersted. You may wanna have a loot at Function.
The first part that I don't understand is
template<Ret (Container::*MemberFunc)(Args...)>
For me a template must be followed by typename. Also, what follows typename, is the thing to be substituted for. I don't see how this template makes Callable templated. I don't know where something goes to in Callable<something>.
Also, what is DeduceImpl? Looks like a function declaration but without a definition.
Also, what Container::*MemberFunc means?
Firstly, templates can also take in non-type parameters as well as with typename and class. In this case:
template<Ret (Container::*MemberFunc)(Args...)>
This is a template taking a function pointer as a parameter, where Ret is the return type, Container::*MemberFunc is the pointer to a specific member function in Container with Args... referencing variadic arguments. This gives the pointer the identifier MemberFunc. I have a feeling the asterisk following the scope resolution operator confused you, as usually you would receive a compiler error if you used these two together in any other situation but in this specific case these two are considered one token ::* representing this kind of template parameter instead of the two :: and *.
For this line:
BindImpl<Container, Ret, Args...> DeduceImpl (Ret (Container::*)(Args...));
It is a function declaration. This is a function named DeduceImpl that will return a BindImpl struct that takes a function pointer as an argument. I'm inferring that this function is the interface by which you bind the function pointer, hence the (probably) shortened names "Deduce Implementation" and "Bind Implementation" From what I've read, this function is only used for decltype, so there's no actual definition for this function.
For how this template is actually being utilized in this line (reformatted for easier reading):
typename decltype(::AIpStack::BindPrivate::DeduceImpl(&EthIpIface::driverSendIp4Packet))
::
template Callable<&EthIpIface::driverSendIp4Packet>(this).toFunction()
This is a template disambiguator created just so the compiler knows that the actual template is being utilized instead of a less-than comparison operator.
You wouldn't write all of this just to use the template. This line was probably written because it's one of the few ways the template is instantiated in the project.
In summary:
template<Ret (Container::*MemberFunc)(Args...)> is a template that takes a function pointer referred to as MemberFunc as a parameter.
DeduceImpl returns a BindImpl struct by taking in the function pointer you want to bind.
I am trying to write a general invocation function.
It has the following syntax:
template<int Index, typename ReturnType, typename... Parameter>
ReturnType invokeGlobalFunction(Parameter... parameters)
{
return invocator->invoke<ReturnType>(Index, parameters...);
}
Next, I try to derive two different function points from it, like this:
registerFunction(::someGlobalFunction, &invokeGlobalFunction<0, void>);
registerFunction(::someOtherFunction, &invokeGlobalFunction<1, int>);
Where someGlobalFunction has the prototype void someGlobalFunction() and someOtherFunction has the prototype int someOtherFunction(int, const char *).
On the first call, it works like a charm, however the second call throws the error: candidate template ignored: deduced conflicting types for parameter 'Parameter' (<int, const char *> vs. <>).
This implies, that the compiler (g++ 7.4.0 on an Ubuntu system btw.) does not overload the invokeGlobalFunction with the different parameter sets like I expected him to.
A note: When I explicitly set the parameter types on the call
registerFunction(::someOtherFunction, &invokeGlobalFunction<1, int, int, const char *>);
the compiler happily takes it, but I'd like to avoid that, if possible.
As a bonus, it would be great, if I could somehow create a unique function each time the index changes, because that would allow me to have functions with identical parameters but differing return types (which is illegal as far as I know).
Thank you.
but I'd like to avoid that, if possible.
Not with template functions, as far I know.
The problem is that a template parameter isn't a single object but a set of object where a function can accept only an object from the set.
When you write
&invokeGlobalFunction<1, int>
you choose a precise function with Index = 1, ReturnType = int and (this is the point) an empty Parameter... list.
Suggestion: if you can, transform invokeGlobalFunction() in a template struct with a template method.
Something as
template <int Index, typename ReturnType>
struct invokeStruct
{
template <typename ... Parameters>
ReturnType operator() (Parameters ... parameters)
{
// ...
}
};
This way you have a set of struct with, in every struct, a set of operator() in it; passing a invokeStruct<1, int>{} as argument, you pass a single object but, inside it, you have available a set of method.
I'm trying to serialize a templated class MState<T> more or less generically. To do so, I have a parent abstract class MVariable which implements several serialization functions with this form:
template <class Serializer, class SerializedType>
void serialize(Serializer& s, const SOME_SPECIFIC_TYPE &t) const;
I want to allow T to be almost anything. Serialization is done in JSON through RapidJSON::Writer. Because of that, I need to use specific member functions (e.g. Writer::String, Writer::Bool, Writer::Uint...) in order to get the proper formatting for each type T.
Serialization of basic types and STL-containers will be provided by MVariable. However, instead of providing every single type (e.g. replacing SOME_SPECIFIC_TYPE by float, double, bool, etc.) I tried to implement a SFINAE-based solution that seems to have some flaws.
I have a set of typedef definitions and serialization functions like this:
class MVariable
{
template <class SerT> using SerializedFloating =
typename std::enable_if<std::is_floating_point<SerT>::value, SerT>::type;
template <class SerT> using SerializedSeqCntr =
typename std::enable_if<is_stl_sequential_container<SerT>::value, SerT>::type;
/* ... and many others. */
/* Serialization of float, double, long double... */
template <class Serializer, class SerializedType>
void serialize(Serializer& s, const SerializedFloating<SerializedType> &t) const {
s.Double(t);
}
/* Serialization of vector<>, dequeue<>, list<> and forward_list<> */
template <class Serializer, class SerializedType>
void serialize(Serializer& s, const SerializedSeqCntr<SerializedType> &t) const {
/* Let's assume we want to serialize them as JSON arrays: */
s.StartArray();
for(auto const& i : t) {
serialize(s, i); // ----> this fails to instantiate correctly.
}
s.EndArray();
}
/* If the previous templates could not be instantiated, check
* whether the SerializedType is a class with a proper serialize
* function:
**/
template <class Serializer, class SerializedType>
void serialize(Serializer&, SerializedType) const
{
/* Check existance of:
* void SerializedType::serialize(Serializer&) const;
**/
static_assert(has_serialize<
SerializedType,
void(Serializer&)>::value, "error message");
/* ... if it exists then we use it. */
}
};
template <class T>
class MState : public MVariable
{
T m_state;
template <class Serializer>
void serialize(Serializer& s) const {
s.Key(m_variable_name);
MVariable::serialize<Serializer, T>(s, m_state);
}
};
The implementation of is_stl_sequential_container is based on this and the implementation of has_serialize is borrowed from here. Both have been checked and seem to work properly:
MState<float> tvar0;
MState<double> tvar1;
MState<std::vector<float> > tvar2;
rapidjson::StringBuffer str_buf;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(str_buf);
writer.StartObject();
tvar0.serialize(writer); /* --> First function is used. Ok! */
tvar1.serialize(writer); /* --> First function is used. Ok! */
tvar2.serialize(writer); /* --> Second function is used, but there's
* substitution failure in the inner call.
**/
writer.EndObject();
However, the recursive serialize call inside the second function, fails to be instantiated. Compiler complains starting with this:
In instantiation of ‘void MVariable::serialize(Serializer&, SerializedType) const
[with Serializer = rapidjson::PrettyWriter<... blah, blah, blah>;
SerializedType = float]’:
The message continues with the static assert error, suggesting that all the previous overloaded template functions failed in their substitutions or that the last one was the best option.
Why is substitution "failing" here for float and not when I try to serialize tvar0 or tvar1?
The problem ...
There are at least two issues in your code.
Firstly, you're explicitly specifying the template arguments in MState::serialize():
MVariable::serialize<Serializer, T>(s, m_state);
but then you're invoking template type deduction inside the SerializedSeqCntr-constrained overload (via serialize(s, i);); this won't work, because those SFINAE checks are non-deduced contexts(*), that is, they do not partecipate in type deduction, the compiler has no way of deducing the SerializedType type.
Either pass the arguments explicitly, as in
serialize<Serializer,std::decay_t<decltype(i)>>(s, i);
or add a deduced SerializedType const& parameter and a sfinae constrained dummy default argument or return type(**).
The second problem is that the 'fallback' overload should precede the constrained overloads possibly invoking it:
template <class Serializer, class SerializedType>
void serialize(Serializer&, SerializedType) const:
template <class Serializer, class SerializedType>
void serialize(Serializer& s, const SerializedSeqCntr<SerializedType> &t);
...
otherwise, name look-up won't find the right serialize() inside the SerializedSeqCntr-constrained overload. Yes, being the function a dependent name, name look-up does happen at instantiation point; however, only names visible in the function body context are considered (unless ADL kicks in).
There could be also a third problem too; the fallback overload is not preferred over the constrained overload just because the former takes SerializedType by value; if this is not the intent, you'll need to further constrain the fallback too.
... and some theory:
(*) to elaborate a bit, when you invoke a function template, you either pass template arguments explicitly (as in foo<bar>()) or let the compiler deduce them from the types of the function arguments (as in foo(some_bar)). Sometimes, this process cannot succeed.
This can happen for three reasons:
there is a substitution failure; that is, a template parameter T has been successfully deduced or given, but it also appears in an expression for which an error would have occurred if spelled out outside the function signature; the function overload is simply ignored; this is what SFINAE is all about.
there is an error while instantiating the types and functions needed to perform substitution; the function is not ignored, the program is ill-formed (if this sounds confusing, this answer may help).
the template argument cannot be deduced, the function overload is ignored; an obvious example is when the template parameter does not appear in any function argument yet is not explicitly specified; another example is when a function argument in which it appears happens to be a non-deduced context, see this answer for an explanation; you'll see that the argument, say, const SerializedFloating<SerializedType>& is indeed non-deduced.
(**) as already said, SFINAE constraints are typically non-deduced; so, if you need type deduction to work, you should pass the to-be-deduced parameter on its own, deducible argument; this is typically done either by adding a dummy default argument or via the return type:
template<typename T>
result_type
foo( T arg, std::enable_if_t<std::is_floating_point<T>::value>* = 0 );
template<typename T>
std::enable_if_t<std::is_floating_point<T>::value, result_type>
foo( T arg );
I just started on C++ so sorry if this is a newbie-ish question. I searched all over the web and didn't find anything about this. In fact I wasn't even sure how to formulate my search...
I saw this code somewhere:
template <class T>
struct SomeStruct
{
SomeStruct() {}
};
And later, this:
int main()
{
SomeStruct<void (Foo::*)(int test)> mStruct;
}
The above compiles just fine.
So if I understood it correctly, "void (Foo::*)(int test)" is a function pointer that points to some function in Foo taking a int as argument and returning void.
How can that be a legal argument for the "class T" parameter?
Any help would be appreciated.
void (Foo::*)(int test) is a type of pointer to member function. A variable of such type can be used to point to member function of class Foo (that returns void and takes a single int argument).
class T is a misnomer there - arbitrary type can be used as a template parameter (the type doesn't have to be declared as class), regardless if the template is declared with template<class T> or template<typename T>.
For this reason I don't use the first form, only the latter.
In context of template parameter list of template declaration, typename and class can be used interchangeably, except you must use class in template template parameters (like template<template<typename, typename> class> before C++1z.
It's a function pointer type. Perfectly legitimate.
The argument doesn't have to be an actual class; the use of class there is misleading, and is equivalent to the clearer typename.
If you want to really be freaked out, look up non-type template arguments, because you can in fact pass an actual function pointer (with a little modification). :)