Choosing type based on lambda signature - c++

I am trying to determine a type based on the signature of a lambda expression.
I've come up with the following code, which works, but I'm wondering if there isn't a simpler way to go about it. I've posted a full working example on ideone.
template <typename T_Callback>
class CallbackType {
private:
template <typename T>
static CallbackFunctionA<T> testlambda(void (T::*op)(A const &) const);
template <typename T>
static CallbackFunctionB<T> testlambda(void (T::*op)(B const &) const);
template <typename T>
static decltype(testlambda<T>(&T::operator())) testany(int);
template <typename T>
static T &testany(...);
public:
typedef decltype(testany<T_Callback>(0)) type;
};
In short, T_Callback can be either:
a lambda of the form [](A const &) { }
a lambda of the form [](B const &) { }
an instance of the class Callback or a class derived therefrom
It T_Callback is a lambda, a wrapper class derived from Callback should be returned (CallbackFunctionA or CallbackFunctionB), otherwise a reference to the callback type itself should be returned.
As I said the above code works just fine, but I'm wondering if it can be simplified, i.e. by removing the need for both testany and testlambda functions.

What you say you want:
a lambda of the form [](A const &) { } shall be mapped to CallbackFuncionA
a lambda of the form [](B const &) { } shall be mapped to CallbackFunctionB
an instance of the class Callback or a class derived therefrom shall be mapped to itself
What you actually have:
A functor having exactly one operator() returning nothing and having one argument of type const A& will be mapped to CallbackFunctionA
A functor having exactly one operator() returning nothing and having one argument of type const B& will be mapped to CallbackFunctionB
Anything else will be mapped to itself
My suggestion: Define a simple forwarder like this, for invoking when possible:
template<class X, class ARG...>
static auto may_invoke(const X& x, ARG&&... arg)
-> decltype(x(std::forward<ARG>(arg)...))
{ return x(std::forward<ARG>(arg)...); }
struct not_invoked {};
template<class X>
constexpr static not_invoked may_invoke(const X&, ...)
{ return {}; }
Also, a tester whether it can be invoked is nice to have:
template<class X, class ARG...>
constexpr bool does_invoke(const X& x, ARG&&... arg)
{ return !std::is_same<not_invoked,
decltype(may_invoke(x, std::forward<ARG>(arg)...))>::value; }
That allows you to at the same time make your code more general and the tests more stringent.

According to your specs, you need:
one step to differentiate (lambda) classes with one defined member operator() from classes derived from Callback (that apparently are assumed not to have exactly one member operator()), for which usage of SFINAE is the appropriate technique (impl with testany).
and another step to differentiate between the different argument types (A and B) of the found member operator (impl with testlamda).
Combining these steps in one would require both accessing the operator() in the call to testany from typedef test as well as resolving if such member function exists, which seems impossible to me.

Related

Parameter pack template class perfect forwarding is not working

I am trying to use perfect forwarding with parameter pack definitions made for a whole class, not just for the specific function.
example:
#include <tuple>
template<typename Ret, typename... Params>
class Example {
public:
Ret call(Params&&... data)
{
}
template <typename ...TParams> Ret
call1(TParams&&... data)
{
}
private:
};
int main() {
Example<void, int, short> example;
int i = 32;
//example.call(i, 0);
example.call1(i, 0);
}
But the compiler makes a difference between call and call1. I thought that both examples shall work. If you uncomment "call" the compiler gives the error:
" rvalue reference to type 'int' cannot bind to lvalue of type 'int'"
My problem exactly:
I wanted to create a call-system where you can derive and override the call function. But you can't override templated functions.
If I use it without the Rvalue-References it's working fine. But since my architecture has at least one deeper call since the calling class is just derived by another class which has the same template with that parameter pack which then is std::forwarded. That's where I came to the topic of perfect forwarding, which is not useable this way.
So the idea was to use it like this:
#include <tuple>
template<typename Ret, typename... Params>
class Base {
public:
Ret call(Params&&... data)
{
}
};
template<typename Ret, typename... Params>
class Derived : public Base<Ret, Params...> {
public:
Ret call(Params&&... data)
{
Base<Ret, Params...>::call(std::forward<Params>(data)...);
}
template <typename ...TParams> Ret
call1(TParams&& ...)
{
}
private:
};
int main() {
Derived<void, int, short> MyDerived;
int i = 32;
//MyDerived.call(i, 0);
MyDerived.call1(i, 0);
}
But of course this does not work.
So using it without "&&" does work but as said with the additional stack-memory per deeper call.
#include <tuple>
template<typename Ret, typename... Params>
class Base {
public:
Ret call(Params... data)
{
}
};
template<typename Ret, typename... Params>
class Derived : public Base<Ret, Params...> {
public:
Ret call(Params... data)
{
Base<Ret, Params...>::call(std::forward<Params>(data)...);
}
template <typename ...TParams> Ret
call1(TParams&& ...)
{
}
private:
};
int main() {
Derived<void, int, short> MyDerived;
int i = 32;
MyDerived.call(i, 0);
MyDerived.call1(i, 0);
}
It works if you pass the parameters by std::move :
#include <tuple>
template<typename Ret, typename... Params>
class Base {
public:
Ret call(Params&&... data)
{
}
};
template<typename Ret, typename... Params>
class Derived : public Base<Ret, Params...> {
public:
Ret call(Params&&... data)
{
Base<Ret, Params...>::call(std::forward<Params>(data)...);
}
template <typename ...TParams> Ret
call1(TParams&& ...)
{
}
private:
};
int main() {
Derived<void, int, short> MyDerived;
int i = 32;
MyDerived.call(std::move(i), std::move(0));
MyDerived.call1(i, 0);
}
Besides this produces a load of code, I do not want the Users of my API to always use std::move, just pass the parameters as normal.
To complexify a bit, an additional case which shall also work the same behaviour but with a composit instead of an inheritance. This leads definetely to an additional stack allocation of the pack expansion for the call of the Composit, which I want to optimize, since this is not needed:
#include <tuple>
template<typename Ret, typename... Params>
class Composit {
public:
Ret call(Params&&... data)
{
}
};
template<typename Ret, typename... Params>
class MainClass {
public:
MainClass() : callee(new Composit<Ret, Params...>())
{}
Ret call(Params&&... data)
{
callee->call(std::forward<Params>(data)...);
}
template <typename ...TParams> Ret
call1(TParams&& ...)
{
}
private:
Composit<Ret, Params...>* callee;
};
int main() {
MainClass<void, int, short> MyClass;
int i = 32;
MyClass.call(std::move(i), std::move(0));
//MyClass.call1(i, 0);
}
Is there any solution to solve this problem without std::move?
Code was run on a clang-compiler: https://godbolt.org/z/vecsqM9sY
You can simply copy the examples inside.
TLDR: template parameter matching against T&& results in one of 4 reference types: T&, T&&, const T&, const T&&. When automatic parameter matching is not possible and you specify the template parameter manually at a class level instead, then you basically have to pick one of these 4 reference types. E.g.: Derived<void, const int&, const short&> MyDerived; Perfect forwarding will still work in the sense it will perfectly forward the type you chose. But it will not directly work if you only want to specify the base type T and want the member function to work for either of these 4 reference types.
More detailed answer following:
Template parameter with perfect forwarding allows all 4 possible reference types of a base type myclass passed to a function template<typename T> A(T&& var) to be forwarded perfectly to another function template<typename T> B(T&& var). But first let's look at how these possible reference types passed to A are matched to T:
myclass& => matches using T=myclass&
const myclass& => matches using T=const myclass&
myclass&& => matches using T=myclass, but T=myclass&& has the same result
const myclass&& => matches using T=const myclass, but T=const myclass&& has the same result
The basic trick in this matching is that you can combine reference types & and && in type declarations and these are resolved as follows:
myclass & && results in myclass &
myclass && && results in myclass &&
myclass && results in myclass &&
First, looking at the possible parameter matchings, then the end results are all references, but the deducted template parameter is one of four: myclass&, const myclass&, myclass, const myclass.
Second, as you can see if you give myclass as template parameter that essentially implies myclass&& as function parameter.
This happens in your example: since you specify only the base type (int, short) as template parameter, this implies an rvalue reference parameter in your member function specification.
I would suggest to avoid using base type names myclass and always choose which reference type you want explicitly, since template parameter T=myclass&& also implies function parameter type myclass&&.
So what about perfect forwarding? If var was an rvalue reference for A then, as a named variable, it automatically gets converted to an lvalue reference when passed to B. Thus you cannot directly forward rvalue references. This can be solved via std::forward which converts an lvalue reference back to an rvalue reference when T=myclass, T=const myclass, T=myclass&& or T=const myclass&&. But this crucially depends on the original template matching for A.
Lastly, one of the problems with the current solution is that it is not flexible when you want to pass both named variables (i) and unnamed variables/explicit values (0). This is because named variables of type T=int can be bound to:
T&
const T&
but not to T&&
While an explicit value (or unnamed variable on the right hand) of type T=int can be bound to:
T&&
const T&&
const T&
but not to T&
So if you are not modifying the variable then T=const int& can be bound to all cases. But when you want to modify the variable then it makes sense to choose int&, but that doesn't allow passing explicit values (like 0).
Though that does not seem to be question here, but I think it would be possible in theory to specify the base type at class level and have templated member functions that would then take one of the 4 reference types. But that would require a more intricate solution and templated member functions in the base class and derived classes.

Reference lost on forwarding (and no auto-conversion to save me) - why?

I'm writing a Factory for producing instances of subclasses of a base class using their name, and using this (templated) factory with my class Foo. Never mind the entire code, but essentially, the factory has a map from string to functions creating instances; and a template parameter controls which arguments these functions take. In my case, the ctor of Foo, and any subclass of foo, takes a const Bar&, and that's what the variadic template for ctor arguments consist of.
I have:
template<typename SubclassKey, typename T, typename... ConstructionArgs>
class Factory {
public:
using Instantiator = T* (*)(ConstructionArgs&&...);
private:
template<typename U>
static T* createInstance(ConstructionArgs&&... args)
{
return new U(std::forward<ConstructionArgs>(args)...);
}
using Instantiators = std::unordered_map<SubclassKey,Instantiator>;
Instantiators subclassInstantiators;
public:
template<typename U>
void registerSubclass(const SubclassKey& subclass_id)
{
static_assert(std::is_base_of<T, U>::value,
"This factory cannot register a class which is is not actually "
"derived from the factory's associated class");
auto it = subclassInstantiators.find(subclass_id);
if (it != subclassInstantiators.end()) {
throw std::logic_error("Repeat registration of the same subclass in this factory.");
}
subclassInstantiators.emplace(subclass_id, &createInstance<U>);
}
};
And by popular demand, here's also...
class Foo {
using FooFactory = Factory<std::string, Foo, const Bar&>;
private:
static FooFactory& getTestFactory() {
static FooFactory kernel_test_factory;
return kernel_test_factory;
}
//...
public:
//...
template <typename U>
static void registerInFactory(const std::string& name_of_u) {
Foo::getTestFactory().registerSubclass<U>(name_of_u);
}
Bar bar;
Foo(const Bar& bar_) : bar(bar_) { };
virtual ~Foo() {}
// ...
};
class NiceFoo : public Foo {
// no ctors and dtors
};
Unfortunately, for some reason, when I call
I get a complaint about the ctor expecting a const Bar& while the argument list I supposedly provide in my createInstance is actually a const Bar.
Questions:
Why is the reference "disappearing"?
Am I doing something wrong? Should I be approaching this matter differently?
GCC error output:
/home/joeuser/myproj/../util/Factory.h(36): error: no instance of constructor "NiceFoo::NiceFoo" matches the argument list
argument types are: (const Bar)
detected during:
instantiation of "T *util::Factory<SubclassKey, T, ConstructionArgs...>::createInstance<U>(ConstructionArgs &&...) [with SubclassKey=std::string, T=Foo, ConstructionArgs=<const Bar &>, U=NiceFoo]"
(59): here
instantiation of "void util::Factory<SubclassKey, T, ConstructionArgs...>::registerSubclass<U>(const SubclassKey &) [with SubclassKey=std::string, T=Foo, ConstructionArgs=<const Bar &>, U=NiceFoo]"
/home/joeuser/myproj/../Foo.h(79): here
instantiation of "void Foo::registerInFactory<U>(const std::string &) [with U=NiceFoo]"
/home/joeuser/myproj/NiceFoo.cpp(122): here
There are two problems in your code: the immediate one, and a fundamental one.
The immediate one is that NiceFoo does not declare any constructors. You claimed that "it inherits Foo's constructors," but that's not the case in the code you've shown. Inheriting constructors would be achieved by this:
class NiceFoo : public Foo {
public:
using Foo::Foo;
}
Without the using declaration, you just have a class with no constructors declared (it will have the copy, move, and parameterless ones generated by default, but no new ones).
There is also a fundamental issue in your design: You're trying to use template parameters of a class template in perfect forwarding. That doesn't work; perfect forwarding relies on template argument deduction, which only happens with function templates.
In other words, this: ConstructionArgs&&... does not create forwarding references, but plain old rvalue references. In the concrete case you've presented, it does not really matter, because ConstructionArgs is const Bar & and reference collapsing makes it work out. But if you had included a value type (and not reference type) among the constructor arguments, createInstance would have been using plain rvalue references and initialisation from an lvalue would have been impossible.
The correct solution to this would be to have ConstructionArgs mirror exactly what the constructor of T expects—in other words, drop the && from use of ConstructionArgs everywhere, and use std::move instead of std::forward inside createInstance. One second thought, the std::forward should be able to stay and do the right thing; basically equivalent to a no-op for lvalue references, and to a std::move for values and rvalue references.

const/non-const member function based on compile time conditions

Let's consider following classes:
struct InputArgument{};
struct SpecialInputArgument{};
struct MoreSpecialInputArgument{};
struct OutputArgument{};
struct SpecialOutputArgument{};
struct MoreSpecialOutputArgument{};
I need to have a member function that accepts all previous classes as arguments and act on them. To simplify the implementation (don't repeat same code over and over) I made the member function template and dispatched the actual code to non-member functions:
template<typename T>
typename std::enable_if<std::is_fundamental<T>::value>::type DoSomething(T&, const InputArgument&)
{
}
template<typename T>
typename std::enable_if<std::is_fundamental<T>::value>::type DoSomething(const T&, OutputArgument&)
{
}
template<typename T>
typename std::enable_if<std::is_fundamental<T>::value>::type DoSomething(T&, const SpecialInputArgument&)
{
}
template<typename T>
typename std::enable_if<std::is_fundamental<T>::value>::type DoSomething(const T&, SpecialOutputArgument&)
{
}
template<typename T>
typename std::enable_if<std::is_fundamental<T>::value>::type DoSomething(T&, const MoreSpecialInputArgument&)
{
}
template<typename T>
typename std::enable_if<std::is_fundamental<T>::value>::type DoSomething(const T&, MoreSpecialOutputArgument&)
{
}
struct MyGloriuosClass
{
template<typename T>
void DoSomething(T& arg)
{
::DoSomething(myIntMember, arg);
::DoSomething(myFloatMember, arg);
}
int myIntMember = 0;
float myFloatMember = 0.f;
};
And this works perfect:
MyGloriuosClass myGloriuosObject;
InputArgument inputArgument;
SpecialInputArgument specialInputArgument;
MoreSpecialInputArgument moreSpecialInputArgument;
OutputArgument outputArgument;
SpecialOutputArgument specialOutputArgument;
MoreSpecialOutputArgument moreSpecialOutputArgument;
myGloriuosObject.DoSomething(inputArgument);
myGloriuosObject.DoSomething(specialInputArgument);
myGloriuosObject.DoSomething(moreSpecialInputArgument);
myGloriuosObject.DoSomething(outputArgument);
myGloriuosObject.DoSomething(specialOutputArgument);
myGloriuosObject.DoSomething(moreSpecialOutputArgument);
Expect in one case, when the object I use is const:
const MyGloriuosClass myConstGloriousObject = MyGloriuosClass();
myConstGloriousObject.DoSomething(outputArgument);
myConstGloriousObject.DoSomething(specialOutputArgument);
myConstGloriousObject.DoSomething(moreSpecialOutputArgument);
As you can see, all the actual code is done in functions that accept const objects when the argument is of type Output so there is no reason to limit this to only non-const objects or to write my member function twice, once as const and once as non-const. In my ideal world I will deduce if the function is const/non-const based on the type trait std::is_base_of of the argument, but I don't know if this is possible or not.
Is it possible to declare a member function const/non-const based on compile time conditions?
A member function is either const, or non-const member function. There is no third option. Classes can define either a const function, a non-const function, or even both, as you know.
What I suspect that you might be missing is that a const member function can be invoked for a non-const class instance.
So, if your member function does not need to modify any other members of the class instance, just declare your member function as a const function, and it can be invoked for either a const or a non-const class instance.

C++: "specializing" a member function template to work for derived classes from a certain base class

I have a base class MessageBase, from which I derive various other message classes, e.g., MessageDerived. I have another class that does some processing on various types of data, including a catchall method template:
struct Process {
void f(int a);
void f(const char* b);
template<typename T> void f(const T &t) { ... }
};
So if I call Process::f on a message object, the template method is called.
Now I want to add custom functionality for for my message classes. I am not allowed to change Process, so I want to derive from it (but even if I could change it, I can't find a way to get the custom functionality). I tried:
struct ProcessDerived : public Process {
void f(const MesaageBase& m) { ... } // Custom functionality for messages.
};
But that only works when I call ProcessDerived::f on a MessageBase object. If I invoke it on a MessageDerived object, the template method gets selected instead.
Is there a way to get the custom function selected on all message classes while letting the template catch all other types?
You need to use SFINAE here. Here is an example (note that it needs c++11 to work):
struct ProcessDerived : public Process
{
template<typename T> void f(const T &t, typename std::conditional<std::is_base_of<MessageBase, T>::value, int, void>::type = 0)
{
/// do something specific
}
template<typename T> void f(const T &t, ...)
{
return Process::f (t);
}
};
};
You can read more about it at http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error
ProcessDerived::f shadows all definitions of Process::f (see here: Overloading rules for inheritance in C++). So calling ProcessDerived::f with a MessageDerived object calls ProcessDerived::f(const MessageBase&) as this is the only visible f, but e.g.
ProcessDerived pd;
pd(42);
won't compile. You need to add a
using Process::f;
in ProcessDerived for the problem you described showing up. The solution proposed by Alex Telishev while I was writing fixes both problems at once, however.

Using decltype in a late specified return in CRTP base class

I'm trying to use decltype in the late specified return of a member function in a CRTP base class and it's erroring with: invalid use of incomplete type const struct AnyOp<main()::<lambda(int)> >.
template<class Op>
struct Operation
{
template<class Foo>
auto operator()(const Foo &foo) const ->
typename std::enable_if<is_foo<Foo>::value,
decltype(static_cast<const Op*>(nullptr)->call_with_foo(foo))>::type
{
return static_cast<const Op*>(this)->call_with_foo(foo);
}
};
template<class Functor>
struct AnyOp : Operation<AnyOp<Functor> >
{
explicit AnyOp(Functor func) : func_(func) {}
template<class Foo>
bool call_with_foo(const Foo &foo) const
{
//do whatever
}
private:
Functor func_;
};
I'm basically trying to move all of the sfinae boiler plate into a base class so I don't need to repeat it for every Operation that I create (currently each operation has 6 different calls and there are ~50 operations so there is quite a lot of repetition with the enable_if's).
I've tried a solution which relied on overloading but one of the types which may be passed is anything that's callable(this can be a regular functor from C++03 or a C++0x lambda) which I bound to a std::function, unfortunately, the overhead from std::function, although very minimal, actually makes a difference in this application.
Is there a way to fix what I currently have or is there a better solution all together to solve this problem?
Thanks.
You are, as another answer describes already, trying to access a member of a class in one of the class' base class. That's going to fail because the member is yet undeclared at that point.
When it instantiates the base class, it instantiates all its member declarations, so it needs to know the return type. You can make the return type be dependent on Foo, which makes it delay the computation of the return type until Foo is known. This would change the base class like the following
// ignore<T, U> == identity<T>
template<typename T, typename Ignore>
struct ignore { typedef T type; };
template<class Op>
struct Operation
{
template<class Foo>
auto operator()(const Foo &foo) const ->
typename std::enable_if<is_foo<Foo>::value,
decltype(static_cast<typename ignore<const Op*, Foo>::type>(nullptr)->call_with_foo(foo))>::type
{
return static_cast<const Op*>(this)->call_with_foo(foo);
}
};
This artificially makes the static_cast cast to a type dependent on Foo, so it does not immediately require a complete Op type. Rather, the type needs to be complete when operator() is instantiated with the respective template argument.
You are trying to refer to a member of a class from one of its own base classes, which will fail since the class's body doesn't exist within its base class. Can you pass the logic for computing the return type of call_with_foo as a metafunction to the base class? Is that logic ever going to be complicated?
Another option, depending on how much flexibility you have in changing your class hierarchy (and remember that you have template typedefs), is to have the wrapper inherit from the implementation class rather than the other way around. For example, you can write a AddParensWrapper<T> that inherits from T and has operator() that forwards to T::call_with_foo. That will fix the dependency problem.