Given this code:
template< class C >
void foo( C const& o ) { o.nosuch(); }
struct Base {};
void foo( Base const& ) {}
struct Derived: Base {};
auto main() -> int
{
Derived d;
foo( d ); // !Invokes template.
}
… I want the call to invoke the overload defined for Base, without having to define an overload or template specialization for Derived.
The goal is to be able to apply foo to all kinds of objects, not just Base (and Derived) objects, with a generic implementation for most kinds of objects.
It would also be nice if an answer explained exactly how overload resolution works in this case, versus how it works with a Derived overload defined.
In the code where this problem manifested, foo above is a function template n_items defined as follows:
template< class Type >
auto n_items( Type const& o )
-> size_t
{ return o.size(); }
template< size_t n >
auto n_items( std::bitset<n> const& o )
-> size_t
{ return o.count(); } // Corresponds to std::set<int>::size()
template< class Type, size_t n >
constexpr
auto n_items( Raw_array_of_<n, Type> const& a )
-> size_t
{ return static_size( a ); }
And the intent is that this should be a catch-all for types that don't define their own n_items overloads.
And for a base and derived class, it should be enough that the base class defines a custom n_items; it would be very redundant to have to define it for every derived class.
How overload resolution works
First we do name lookup and template type deduction. For foo(d), this gives us two options:
foo(Derived const&), with [C = Derived]
foo(Base const&)
These are our viable candidates.
Then we determine which one of the overloads is the best viable candidate. This is done first by looking at conversion sequences. In this case, (1) is an Exact Match whereas (2) involves a derived-to-base conversion which has Conversion rank (see this table). Since that ranks worse, candidate (1) is preferred and is thus deemed the best viable candidate.
How to do what you really want
The simplest way is to simply add a third overload for Derived:
foo(Derived const&)
Here, both (1) and (3) would be equivalent in terms of conversion sequence. But functions that aren't templates are preferred to functions that are templates (think of it as picking the most specific option), so (3) would be selected.
But you don't want to do that. So the options are either: make (1) not work for Derived or make (2) work for Derived too in a way that's preferred.
Disable the general template
We can use SFINAE to simply exclude anything derived from Base:
template <class T, class = std::enable_if_t<!std::is_convertible<T*, Base*>::value>
void foo(T const& ); // new (1)
void foo(Base const& ); // same (2)
Now, (1) is no longer a viable candidate for the match, so (2) is trivally preferred.
Redo the overloads so that the Base is preferred
Taking a tip out of Xeo's book, we can restructure the overloads thusly:
template <int I> struct choice : choice<I + 1> { };
template <> struct choice<10> { };
struct otherwise { otherwise(...) {} };
template <class T> void foo(T const& val) { foo_impl(val, choice<0>{}); }
And now we can prefer those types derived from Base:
template <class T, class = std::enable_if_t<std::is_convertible<T*, Base*>::value>>
void foo_impl(T const& val, choice<0> ); // new (2)
template <class T>
void foo_impl(T const& val, otherwise ); // new (1)
This changes the mechanics of how overload resolution works and is worth going through into the separate cases.
Calling foo(d) means we're calling foo_impl(d, choice<0> ) and gives us two viable candidates:
foo_impl(Derived const&, choice<0> ), with [T = Derived]
foo_impl(Derived const&, otherwise ), with [T = Derived]
Here, the first argument is identical, but for the second argument, the first overload is an Exact Match while the second argument requires a Conversion via a variadic argument. otherwise will always be the word choice as a result, so the first overload is preferred. Exactly what we want.
Calling foo(not_a_base), on the other hand, would only give us one viable candidate:
foo_impl(NotABase const&, otherwise ), with [T = NotABase]
The other one was removed due to the deduction failure. So this is trivially the best viable candidate, and again we get exactly what we want.
For your specific question, I'd write something like:
template <class T>
size_t n_items(T const& cont) { return n_items(cont, choice<0>{}); }
with:
// has count?
template <class T>
auto n_items(T const& cont, choice<0> ) -> decltype(cont.count()) {
return cont.count();
}
// else, has size?
template <class T>
auto n_items(T const& cont, choice<1> ) -> decltype(cont.size()) {
return cont.size();
}
// else, use static_size
template <class T>
size_t n_items(T const& cont, otherwise )
return static_size(cont);
}
Put all the non-template functions in some namespace:
namespace Foo {
void foo( Base const& ) {}
}
Then define the function template thusly:
template <typename C>
auto foo_aux(C const& c, int) -> decltype(Foo::foo(c)) {
return Foo::foo(c);}
template <typename C>
void foo_aux(C const& c, ...) {
c.nosuch();}
template <typename C>
void foo(C const& c) {foo_aux(c, 0);}
Demo. This will call the general overload if and only if none of the non-template overloads match (or are ambiguous).
The main technical problem is that a general function template foo (say), instantiated for Derived argument, is a better match than the overload for Base argument.
Brute force solution (redundancy).
One simple possible solution is to require overloads of all the relevant functions for every class derived from a Base.
However, this breaks the DRY rule, Don't Repeat Yourself.
Needless redundancy in code leads to maintenance problems.
Namespace for SFINAE.
Another possible solution, suggested by Columbo, is to “put all the non-template functions in some [specific single] namespace”, which allows a general function template to use SFINAE to detect if a relevant overload exists.
This solution avoids the redundancy, and as I recall it is the kind of solution used for boost::intrusive_ptr, that client code must specialize the functionality in some specific namespace.
However, since it imposes a restriction on client code it feels not perfect to me.
Extra argument to direct overload resolution.
A third possible solution, suggested by Barry, is to let the base class implementation have an associated overload, or just a differently named function, that has an extra argument that serves to direct the overload resolution, and that is called by by a general function template.
This is technically a solution but IMO it's not clean: the client code invocations don't match the overloads that must be provided.
And so again there is a potential maintenance problem.
Invoking the function template from a simple converting overload.
Johannes Schaub suggested a fourth possible solution, which allows clean, simple client code, namely to let an ordinary overload call the function template, but where that general implementation overload has a formal argument type that
introduces a user defined conversion, which makes this a worse match than direct per-class overloads, and
has a templated constructor that picks up the actual argument type.
Johannes' original ingenious in-comment idea assumed a fixed function result type and exactly one argument. Generalizing that short idea description to arbitrary result type was not entirely trivial for me because that result type can depend on the actual argument, and one tends to first of all try to automate things like that. Likewise, generalizing to arbitrary number of arguments, with the first one acting as a this argument, was not 100% trivial for me. Johannes would no doubt have no problems doing this, and probably in a more neat way than I could do it. Anyway, my code:
#include <functional>
#include <utility>
//-------------------------------------- General machinery:
template< class Impl_func, class Result, class... Args >
class Invoker_of_
{
private:
std::function< auto(Args...) -> Result > f_;
public:
auto operator()( Args... args ) const -> Result { return f_( args... ); }
template< class Type >
Invoker_of_( Type& o )
: f_(
[&]( Args... args )
-> Result
{ return Impl_func{}.operator()( o, args... ); }
)
{}
};
//-------------------------------------- General template 1 (foo):
struct Foo_impl
{
template< class Type >
auto operator()( Type& o )
-> int
{ return o.foomethod(); }
};
auto foo( Invoker_of_<Foo_impl, int> const invoker )
-> int
{ return invoker(); }
//-------------------------------------- General template 2 (bar):
struct Bar_impl
{
template< class Type >
auto operator()( Type& o, int const arg1 )
-> int
{ return o.barmethod( arg1 ); }
};
auto bar( Invoker_of_<Bar_impl, int, int> const invoker, int const arg1 )
-> int
{ return invoker( arg1 ); }
//--------------------------------------- Usage examples:
struct Base {};
auto foo( Base const& ) -> int { return 101;}
auto bar( Base const&, int x ) -> int { return x + 2; }
struct Derived: Base {};
struct Other
{
auto foomethod() -> int { return 201; }
auto barmethod( int const x ) -> int { return x + 2; }
};
//--------------------------------------- Test driver:
#include <iostream>
using namespace std;
auto main() -> int
{
Derived d;
int const r1 = foo( d ); // OK, invokes non-template overload.
int const r2 = bar( d, 100 ); // OK, invokes non-template overload.
cout << r1 << " " << r2 << endl;
Other o;
int const r3 = foo( o ); // OK, invokes the general template.
int const r4 = bar( o, 200 ); // OK, invokes the general template.
cout << r3 << " " << r4 << endl;
}
I have not attempted to support rvalue reference arguments.
Output:
101 102
201 202
A variation Johannes Schaub's suggestion, which appears to yield the most clean usage, written up as code for the example at the start:
//-------------------------------------- Machinery:
template< class Type >
auto foo_impl( Type& o ) -> int { return o.method(); }
struct Invoker_of_foo_impl
{
int result;
template< class Type >
Invoker_of_foo_impl( Type& o ): result( foo_impl( o ) ) {}
};
auto foo( Invoker_of_foo_impl const invoker ) -> int { return invoker.result; }
//--------------------------------------- Usage:
struct Base {};
auto foo( Base const& ) -> int { return 6*7;}
struct Derived: Base {};
struct Other { auto method() -> int { return 0b101010; } };
auto main() -> int
{
Derived d;
foo( d ); // OK, invokes non-template.
Other o;
foo( o ); // OK, invokes template
}
I'm not sure I got the problem, but can't you use simply SFINAE and the is_base_of trait?
Using them, catch all functions are automatically excluded when the function resolution takes place for a class that is derived from Base and the best match is the non-template one.
Moreover, such a solution looks to me as far simpler than all the other... That's why I'm pretty sure that I didn't get the problem!! :-)
Anyway, it follows a working example:
#include<type_traits>
#include<iostream>
struct Base {};
auto foo( Base const& ) -> int {return 101;}
auto bar( Base const&, int x ) -> int {return x + 2; }
template<class T, typename = typename std::enable_if<not std::is_base_of<Base, T>::value>::type>
auto foo(T & t) -> int {
return t.foomethod();
}
template<class T, typename = typename std::enable_if<not std::is_base_of<Base, T>::value>::type>
auto bar(T & t, int i) -> int {
return t.barmethod(i);
}
struct Derived: Base {};
struct Other
{
auto foomethod() -> int { return 201; }
auto barmethod( int const x ) -> int { return x + 2; }
};
#include <iostream>
using namespace std;
auto main() -> int
{
Derived d;
int const r1 = foo( d ); // Invokes the Base arg overload.
int const r2 = bar( d, 100 ); // Invokes the Base arg overload.
cout << r1 << " " << r2 << endl;
Other o;
int const r3 = foo( o ); // Invokes the general implementation.
int const r4 = bar( o, 200 ); // Invokes the general implementation.
cout << r3 << " " << r4 << endl;
}
Let me know if I've misunderstood the problem, in that case I'm dropping the answer.
Related
I just watched Stephan T. Lavavej talk at CppCon 2018 on "Class Template Argument Deduction", where at some point he incidentally says:
In C++ type information almost never flows backwards ... I had to say "almost" because there's one or two cases, possibly more but very few.
Despite trying to figure out which cases he might be referring to, I couldn't come up with anything. Hence the question:
In which cases the C++17 standard mandates that type information propagate backwards?
Here is at least one case:
struct foo {
template<class T>
operator T() const {
std::cout << sizeof(T) << "\n";
return {};
}
};
if you do foo f; int x = f; double y = f;, type information will flow "backwards" to figure out what T is in operator T.
You can use this in a more advanced way:
template<class T>
struct tag_t {using type=T;};
template<class F>
struct deduce_return_t {
F f;
template<class T>
operator T()&&{ return std::forward<F>(f)(tag_t<T>{}); }
};
template<class F>
deduce_return_t(F&&)->deduce_return_t<F>;
template<class...Args>
auto construct_from( Args&&... args ) {
return deduce_return_t{ [&](auto ret){
using R=typename decltype(ret)::type;
return R{ std::forward<Args>(args)... };
}};
}
so now I can do
std::vector<int> v = construct_from( 1, 2, 3 );
and it works.
Of course, why not just do {1,2,3}? Well, {1,2,3} isn't an expression.
std::vector<std::vector<int>> v;
v.emplace_back( construct_from(1,2,3) );
which, admittedly, require a bit more wizardry: Live example. (I have to make the deduce return do a SFINAE check of F, then make the F be SFINAE friendly, and I have to block std::initializer_list in deduce_return_t operator T.)
Stephan T. Lavavej explained the case he was talking about in a tweet:
The case I was thinking of is where you can take the address of an overloaded/templated function and if it’s being used to initialize a variable of a specific type, that will disambiguate which one you want. (There’s a list of what disambiguates.)
we can see examples of this from cppreference page on Address of overloaded function, I have excepted a few below:
int f(int) { return 1; }
int f(double) { return 2; }
void g( int(&f1)(int), int(*f2)(double) ) {}
int main(){
g(f, f); // selects int f(int) for the 1st argument
// and int f(double) for the second
auto foo = []() -> int (*)(int) {
return f; // selects int f(int)
};
auto p = static_cast<int(*)(int)>(f); // selects int f(int)
}
Michael Park adds:
It's not limited to initializing a concrete type, either. It could also infer just from the number of arguments
and provides this live example:
void overload(int, int) {}
void overload(int, int, int) {}
template <typename T1, typename T2,
typename A1, typename A2>
void f(void (*)(T1, T2), A1&&, A2&&) {}
template <typename T1, typename T2, typename T3,
typename A1, typename A2, typename A3>
void f(void (*)(T1, T2, T3), A1&&, A2&&, A3&&) {}
int main () {
f(&overload, 1, 2);
}
which I elaborate a little more here.
I believe in static casting of overloaded functions the flow goes the opposite direction as in usual overload resolution. So one of those is backwards, I guess.
There are a few questions on SO that address passing function pointers as parameters/arguments (here, here, here, etc.). In fact, I asked a related question the other day. However, this question is a little different.
My problem is that I am writing a class that I want to be extremely flexible.
What I have now works for non-member functions. It is posted below
template <typename T>
class MyClass
{
private:
typedef double (*firstFunctionPtr) (const T &var);
typedef bool (*secondFunctionPtr)(const T &var);
// Function pointers as member variables
firstFunctionPtr _firstFunc;
secondFunctionPtr _secondFunc;
public:
inline MyClass(firstFunctionPtr firstFunc,
secondFunctionPtr secondFunc);
};
template<typename T>
MyClass<T>::MyClass(firstFunctionPtr firstFunc, secondFunctionPtr secondFunc) :
_firstFunc(firstFunc),
_secondFunc(secondFunc),
{}
However, this falls apart when I need to initialize with a pointer to a member function of some other, arbitrary, class, which, unfortunately for me, happens to be a common use case for my purposes.
This answer suggests that
In a proper C++ interface you might want to have a look at having your function take templated argument for function objects to use arbitrary class types.
However, I have not been able to make this compile. I've tried templating my typedefs (using the C++11 aliasing approach), and I've tried adding a second template parameter to the class to handle the calling class of those member functions, but neither approach has worked.
This Q/A seems to be getting towards what I'm trying to do, but I can't make heads or tails of it.
Can someone please explain how I might modify my class to handle arbitrary member functions pointers being passed in?
Furthermore, is it possible to make it so that it can handle either arbitrary member functions or non-member functions?
Lastly, is it possible to do this with templates?
For the record, I'm trying to avoid using the functional header, but it may be a fool's errand not to use it.
If you want MyClass to be a template that can hold both free function
pointers of types:
double (*)(const T &var);
bool (*)(const T &var);
for some parameter type T, or alternatively member-function
pointers of types:
double (C::*)(const T &var);
bool (C::*)(const T &var);
for some parameter types C and T then, MyClass must be parameterized
by both T and C and you require two specializations:
Where C is some non-class type
Where C is any class type
In case (1), the non-class type C cannot possibly have member functions,
so that one will implement the free-function pointer specialization.
In case (2), the class C could be one that has member functions, so that one
will implement the member-function pointer specialization.
The obvious choice for a non-class type C is void. So we can make C
default to void:
Primary template
template<typename T, typename C = void>
struct MyClass;
So that:
MyClass<T>
will be the free function pointer specialization for T, and:
MyClass<T,C>
for any C other than void, will be the member-function pointer specialization.
As you may know you can use std::enable_if
and SFINAE to make the compiler
chose one specialization of a class template or another, depending on whether one
of its template parameters U satisfies some compiletime test. You could take
that approach here, but another one is available that does not require that apparatus:
Starting with the primary template, we would just like to have:
Free function specialization
template<typename T>
struct MyClass<T>
{
... for free function pointers ...
};
and:
Member function specialization
template<typename T, typename C>
struct MyClass<T,C>
{
... for member function pointers ...
};
But we can't have just that, because the member function "specialization" has exactly
the same template parameters as the primary template. Which means it isn't
a specialization, and the compiler won't allow it.
You can easily remove that problem, however, simply by giving the primary
template one more defaulting template parameter that it doesn't need, but whose
presence allows both those specializations to stand.
New primary template
template <typename T, typename C = void, typename Default = void>
struct MyClass;
So here is an illustrative solution:
// Primary template
template <typename T, typename C = void, typename Default = void>
struct MyClass;
// Free function specialization
template <typename T>
struct MyClass<T>
{
using firstFunctor_t = double(*)(T const &);
using secondFunctor_t = bool(*)(T const &);
MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc)
: _firstFunc(firstFunc),
_secondFunc(secondFunc)
{}
double callFirst(T const & var) {
return _firstFunc(var);
}
bool callSecond(T const & var) {
return _secondFunc(var);
}
private:
firstFunctor_t _firstFunc;
secondFunctor_t _secondFunc;
};
// Member function specialization
template <typename T, typename C>
struct MyClass<T,C>
{
using firstFunctor_t = double(C::*)(T const &);
using secondFunctor_t = bool(C::*)(T const &) const;
MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc)
: _firstFunc(firstFunc),
_secondFunc(secondFunc)
{}
double callFirst(C & obj, T const & var) {
return (obj.*_firstFunc)(var);
}
double callFirst(C const & obj, T const & var) {
auto & o = const_cast<C&>(obj);
return (o.*_firstFunc)(var);
}
bool callSecond(C & obj, T const & var) {
return (obj.*_secondFunc)(var);
}
bool callSecond(C const & obj, T const & var) {
auto & o = const_cast<C&>(obj);
return (o.*_secondFunc)(var);
}
private:
firstFunctor_t _firstFunc;
secondFunctor_t _secondFunc;
};
In the member function specialization, notice a couple of points that you might
not have considered:-
I decided that the second member function I want to store shall be a
const member function. It's more than likely that a member function of C
that take a T const & argument and returns bool will be a const member
function, isn't it? And if so, then that const-ness has to be part of
the member-function type definition that I use in the specialization:
using secondFunctor_t = bool(C::*)(T const &) const;
or attempts to instantiate the specialization with any bool (C::*)(T const &) const
will fail to compile.
Also, I have provided two overloads for each of MyClass<T,C>::callFirst
and MyClass<T,C>::callSecond, one with arguments:
C & obj, T const & var
and another with arguments:
C const & obj, T const & var
Without the second, attempts to call either MyClass<T,C>::callFirst
or MyClass<T,C>::callSecond with an obj that is const will fail to
compile.
For program to demo this solution you can append:
#include <iostream>
#include <string>
double foo(std::string const & s)
{
return std::stod(s);
}
bool bar(std::string const & s)
{
return s.size() > 0;
}
struct SomeClass
{
SomeClass(){};
double foo(std::string const & s) {
return ::foo(s);
}
bool bar(std::string const & s) const {
return ::bar(s);
}
};
int main()
{
MyClass<std::string> my0{foo,bar};
std::cout << std::boolalpha;
std::cout << my0.callFirst("1.11") << std::endl;
std::cout << my0.callSecond("Hello World") << std::endl;
MyClass<std::string,SomeClass> my1{&SomeClass::foo,&SomeClass::bar};
SomeClass thing;
std::cout << my1.callFirst(thing,"2.22") << std::endl;
std::cout << my1.callSecond(thing,"Hello World") << std::endl;
SomeClass const constThing;
std::cout << my1.callFirst(constThing,"3.33") << std::endl;
std::cout << my1.callSecond(constThing,"Hello World") << std::endl;
return 0;
}
See it live
You said that you want this template to be "extremely flexible". The
illustrated solution is fitted to your example, but you might be
interested in know that it isn't nearly as flexible as you could get.
For both free functions and member functions, with additional variadic template
parameters, your template could store and call [member] functions with
arbitary return types and arbitary numbers of arguments of arbitrary types.
See this question and
answer.
I will sugest to create a helper object which will store the type you want to work with:
template <typename RETURN, typename TYPE, typename CLASS>
struct function_pointer
{ using type_t = RETURN (CLASS::*)(const TYPE &); };
template <typename RETURN, typename TYPE>
struct function_pointer<RETURN, TYPE, std::nullptr_t>
{ using type_t = RETURN (*)(const TYPE &); };
This type will create a member-function-pointer if a class is provided as third parameter and a function-pointer otherwise. Now, we can use this helper in MyClass:
template <typename T, typename CLASS = std::nullptr_t>
class MyClass
{
using firstFunctionPtr = typename function_pointer<double, T, CLASS>::type_t;
using secondFunctionPtr = typename function_pointer<bool, T, CLASS>::type_t;
// Function pointers as member variables
firstFunctionPtr _firstFunc;
secondFunctionPtr _secondFunc;
public:
inline MyClass(firstFunctionPtr firstFunc, secondFunctionPtr secondFunc) :
_firstFunc(firstFunc),
_secondFunc(secondFunc)
{}
void call_first(CLASS &c, const T&v) { (c.*_firstFunc)(v); }
void call_second(CLASS &c, const T&v) { (c.*_secondFunc)(v); }
void call_first(const T&v) { (_firstFunc)(v); }
void call_second(const T&v) { (_secondFunc)(v); }
};
I've added call_* functions just to show a use case, which will be as below:
// Some class with the expected function signatures
struct S1
{
int i = 0;
double d(const int &) { std::cout << i << ' ' << __PRETTY_FUNCTION__ << '\n'; return{}; }
bool b(const int &) { std::cout << i << ' ' << __PRETTY_FUNCTION__ << '\n'; return{}; }
};
// Another class with the expected function signatures
struct S2
{
double d(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; }
bool b(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; }
};
// Free function with which could have the expected function signature
template <typename R>
R f(const int &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return{}; }
Using MyClass with an arbitrary class (S1):
S1 a{1}, b{2};
S2 c, d;
MyClass<int, S1> MCiS1(&S1::d, &S1::b);
MCiS1.call_first(a, 111); // Prints -> 1 double S1::d(const int&)
MCiS1.call_second(b, 222); // Prints -> 2 bool S1::b(const int&)
MCiS1.call_first(c, 111); // Error decltype(c) is not S1.
MCiS1.call_second(d, 222); // Error decltype(d) is not S1.
Using MyClass with a different class (S2):
MyClass<int, S2> MCiS2(&S2::d, &S2::b);
MCiS2.call_first(c, 111); // Prints -> double S2::d(const int&)
MCiS2.call_second(d, 222); // Prints -> bool S2::b(const int&)
MCiS2.call_first(a, 111); // Error decltype(c) is not S2.
MCiS2.call_second(b, 222); // Error decltype(d) is not S2.
Using MyClass with non-member functions:
MyClass<int> MCi(f<double>, f<bool>);
MCi.call_first(111); // Prints -> R f(const int&) [with R = double]
MCi.call_second(222); // Prints -> R f(const int&) [with R = bool]
Check the live demo Here.
All you need to do is bind the object instance for the member function pointer as a first argument.
struct foo {
float bar1(const type &var);
bool bar2(const type &var);
};
foo my_foo;
auto f1 = std::bind(&foo::bar1, my_foo, _1);
auto f2 = std::bind(&foo::bar2, my_foo, _1);
MyClass<type> my_obj(f1, f2);
So, suppose I want to type erase using type erasure.
I can create pseudo-methods for variants that enable a natural:
pseudo_method print = [](auto&& self, auto&& os){ os << self; };
std::variant<A,B,C> var = // create a variant of type A B or C
(var->*print)(std::cout); // print it out without knowing what it is
My question is, how do I extend this to a std::any?
It cannot be done "in the raw". But at the point where we assign to/construct a std::any we have the type information we need.
So, in theory, an augmented any:
template<class...OperationsToTypeErase>
struct super_any {
std::any data;
// or some transformation of OperationsToTypeErase?
std::tuple<OperationsToTypeErase...> operations;
// ?? what for ctor/assign/etc?
};
could somehow automatically rebind some code such that the above type of syntax would work.
Ideally it would be as terse in use as the variant case is.
template<class...Ops, class Op,
// SFINAE filter that an op matches:
std::enable_if_t< std::disjunction< std::is_same<Ops, Op>... >{}, int>* =nullptr
>
decltype(auto) operator->*( super_any<Ops...>& a, any_method<Op> ) {
return std::get<Op>(a.operations)(a.data);
}
Now can I keep this to a type, yet reasonably use the lambda syntax to keep things simple?
Ideally I want:
any_method<void(std::ostream&)> print =
[](auto&& self, auto&& os){ os << self; };
using printable_any = make_super_any<&print>;
printable_any bob = 7; // sets up the printing data attached to the any
int main() {
(bob->*print)(std::cout); // prints 7
bob = 3.14159;
(bob->*print)(std::cout); // prints 3.14159
}
or similar syntax. Is this impossible? Infeasible? Easy?
This is a solution that uses C++14 and boost::any, as I don't have a C++17 compiler.
The syntax we end up with is:
const auto print =
make_any_method<void(std::ostream&)>([](auto&& p, std::ostream& t){ t << p << "\n"; });
super_any<decltype(print)> a = 7;
(a->*print)(std::cout);
which is almost optimal. With what I believe to be simple C++17 changes, it should look like:
constexpr any_method<void(std::ostream&)> print =
[](auto&& p, std::ostream& t){ t << p << "\n"; };
super_any<&print> a = 7;
(a->*print)(std::cout);
In C++17 I'd improve this by taking a auto*... of pointers to any_method instead of the decltype noise.
Inheriting publicly from any is a bit risky, as if someone takes the any off the top and modifies it, the tuple of any_method_data will be out of date. Probably we should just mimic the entire any interface rather than inherit publicly.
#dyp wrote a proof of concept in comments to the OP. This is based off his work, cleaned up with value-semantics (stolen from boost::any) added. #cpplearner's pointer-based solution was used to shorten it (thanks!), and then I added the vtable optimization on top of that.
First we use a tag to pass around types:
template<class T>struct tag_t{constexpr tag_t(){};};
template<class T>constexpr tag_t<T> tag{};
This trait class gets the signature stored with an any_method:
This creates a function pointer type, and a factory for said function pointers, given an any_method:
template<class any_method, class Sig=any_sig_from_method<any_method>>
struct any_method_function;
template<class any_method, class R, class...Args>
struct any_method_function<any_method, R(Args...)>
{
using type = R(*)(boost::any&, any_method const*, Args...);
template<class T>
type operator()( tag_t<T> )const{
return [](boost::any& self, any_method const* method, Args...args) {
return (*method)( boost::any_cast<T&>(self), decltype(args)(args)... );
};
}
};
Now we don't want to store a function pointer per operation in our super_any. So we bundle up the function pointers into a vtable:
template<class...any_methods>
using any_method_tuple = std::tuple< typename any_method_function<any_methods>::type... >;
template<class...any_methods, class T>
any_method_tuple<any_methods...> make_vtable( tag_t<T> ) {
return std::make_tuple(
any_method_function<any_methods>{}(tag<T>)...
);
}
template<class...methods>
struct any_methods {
private:
any_method_tuple<methods...> const* vtable = 0;
template<class T>
static any_method_tuple<methods...> const* get_vtable( tag_t<T> ) {
static const auto table = make_vtable<methods...>(tag<T>);
return &table;
}
public:
any_methods() = default;
template<class T>
any_methods( tag_t<T> ): vtable(get_vtable(tag<T>)) {}
any_methods& operator=(any_methods const&)=default;
template<class T>
void change_type( tag_t<T> ={} ) { vtable = get_vtable(tag<T>); }
template<class any_method>
auto get_invoker( tag_t<any_method> ={} ) const {
return std::get<typename any_method_function<any_method>::type>( *vtable );
}
};
we could specialize this for a cases where the vtable is small (for example, 1 item), and use direct pointers stored in-class in those cases for efficiency.
Now we start the super_any. I use super_any_t to make the declaration of super_any a bit easier.
template<class...methods>
struct super_any_t;
This searches the methods that the super any supports for SFINAE:
template<class super_any, class method>
struct super_method_applies : std::false_type {};
template<class M0, class...Methods, class method>
struct super_method_applies<super_any_t<M0, Methods...>, method> :
std::integral_constant<bool, std::is_same<M0, method>{} || super_method_applies<super_any_t<Methods...>, method>{}>
{};
This is the pseudo-method pointer, like print, that we create globally and constly.
We store the object we construct this with inside the any_method. Note that if you construct it with a non-lambda things can get hairy, as the type of this any_method is used as part of the dispatch mechanism.
template<class Sig, class F>
struct any_method {
using signature=Sig;
private:
F f;
public:
template<class Any,
// SFINAE testing that one of the Anys's matches this type:
std::enable_if_t< super_method_applies< std::decay_t<Any>, any_method >{}, int>* =nullptr
>
friend auto operator->*( Any&& self, any_method const& m ) {
// we don't use the value of the any_method, because each any_method has
// a unique type (!) and we check that one of the auto*'s in the super_any
// already has a pointer to us. We then dispatch to the corresponding
// any_method_data...
return [&self, invoke = self.get_invoker(tag<any_method>), m](auto&&...args)->decltype(auto)
{
return invoke( decltype(self)(self), &m, decltype(args)(args)... );
};
}
any_method( F fin ):f(std::move(fin)) {}
template<class...Args>
decltype(auto) operator()(Args&&...args)const {
return f(std::forward<Args>(args)...);
}
};
A factory method, not needed in C++17 I believe:
template<class Sig, class F>
any_method<Sig, std::decay_t<F>>
make_any_method( F&& f ) {
return {std::forward<F>(f)};
}
This is the augmented any. It is both an any, and it carries around a bundle of type-erasure function pointers that change whenever the contained any does:
template<class... methods>
struct super_any_t:boost::any, any_methods<methods...> {
private:
template<class T>
T* get() { return boost::any_cast<T*>(this); }
public:
template<class T,
std::enable_if_t< !std::is_same<std::decay_t<T>, super_any_t>{}, int>* =nullptr
>
super_any_t( T&& t ):
boost::any( std::forward<T>(t) )
{
using dT=std::decay_t<T>;
this->change_type( tag<dT> );
}
super_any_t()=default;
super_any_t(super_any_t&&)=default;
super_any_t(super_any_t const&)=default;
super_any_t& operator=(super_any_t&&)=default;
super_any_t& operator=(super_any_t const&)=default;
template<class T,
std::enable_if_t< !std::is_same<std::decay_t<T>, super_any_t>{}, int>* =nullptr
>
super_any_t& operator=( T&& t ) {
((boost::any&)*this) = std::forward<T>(t);
using dT=std::decay_t<T>;
this->change_type( tag<dT> );
return *this;
}
};
Because we store the any_methods as const objects, this makes making a super_any a bit easier:
template<class...Ts>
using super_any = super_any_t< std::remove_const_t<std::remove_reference_t<Ts>>... >;
Test code:
const auto print = make_any_method<void(std::ostream&)>([](auto&& p, std::ostream& t){ t << p << "\n"; });
const auto wprint = make_any_method<void(std::wostream&)>([](auto&& p, std::wostream& os ){ os << p << L"\n"; });
const auto wont_work = make_any_method<void(std::ostream&)>([](auto&& p, std::ostream& t){ t << p << "\n"; });
struct X {};
int main()
{
super_any<decltype(print), decltype(wprint)> a = 7;
super_any<decltype(print), decltype(wprint)> a2 = 7;
(a->*print)(std::cout);
(a->*wprint)(std::wcout);
// (a->*wont_work)(std::cout);
double d = 4.2;
a = d;
(a->*print)(std::cout);
(a->*wprint)(std::wcout);
(a2->*print)(std::cout);
(a2->*wprint)(std::wcout);
// a = X{}; // generates an error if you try to store a non-printable
}
live example.
The error message when I try to store a non-printable struct X{}; inside the super_any seems reasonable at least on clang:
main.cpp:150:87: error: invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'X')
const auto x0 = make_any_method<void(std::ostream&)>([](auto&& p, std::ostream& t){ t << p << "\n"; });
this happens the moment you try to assign the X{} into the super_any<decltype(x0)>.
The structure of the any_method is sufficiently compatible with the pseudo_method that acts similarly on variants that they can probably be merged.
I used a manual vtable here to keep the type erasure overhead to 1 pointer per super_any. This adds a redirection cost to every any_method call. We could store the pointers directly in the super_any very easily, and it wouldn't be hard to make that a parameter to super_any. In any case, in the 1 erased method case, we should just store it directly.
Two different any_methods of the same type (say, both containing a function pointer) spawn the same kind of super_any. This causes problems at lookup.
Distinguishing between them is a bit tricky. If we changed the super_any to take auto* any_method, we could bundle all of the identical-type any_methods up in the vtable tuple, then do a linear search for a matching pointer if there are more than 1. The linear search should be optimized away by the compiler unless you are doing something crazy like passing a reference or pointer to which particular any_method we are using.
That seems beyond the scope of this answer, however; the existence of that improvement is enough for now.
In addition, a ->* that takes a pointer (or even reference!) on the left hand side can be added, letting it detect this and pass that to the lambda as well. This can make it truly an "any method" in that it works on variants, super_anys, and pointers with that method.
With a bit of if constexpr work, the lambda can branch on doing an ADL or a method call in every case.
This should give us:
(7->*print)(std::cout);
((super_any<&print>)(7)->*print)(std::cout); // C++17 version of above syntax
((std::variant<int, double>{7})->*print)(std::cout);
int* ptr = new int(7);
(ptr->*print)(std::cout);
(std::make_unique<int>(7)->*print)(std::cout);
(std::make_shared<int>(7)->*print)(std::cout);
with the any_method just "doing the right thing" (which is feeding the value to std::cout <<).
Here's my solution. It looks shorter than Yakk's, and it does not use std::aligned_storage and placement new. It additionally supports stateful and local functors (which implies that it might never be possible to write super_any<&print>, since print could be a local variable).
any_method:
template<class F, class Sig> struct any_method;
template<class F, class Ret, class... Args> struct any_method<F,Ret(Args...)> {
F f;
template<class T>
static Ret invoker(any_method& self, boost::any& data, Args... args) {
return self.f(boost::any_cast<T&>(data), std::forward<Args>(args)...);
}
using invoker_type = Ret (any_method&, boost::any&, Args...);
};
make_any_method:
template<class Sig, class F>
any_method<std::decay_t<F>,Sig> make_any_method(F&& f) {
return { std::forward<F>(f) };
}
super_any:
template<class...OperationsToTypeErase>
struct super_any {
boost::any data;
std::tuple<typename OperationsToTypeErase::invoker_type*...> operations = {};
template<class T, class ContainedType = std::decay_t<T>>
super_any(T&& t)
: data(std::forward<T>(t))
, operations((OperationsToTypeErase::template invoker<ContainedType>)...)
{}
template<class T, class ContainedType = std::decay_t<T>>
super_any& operator=(T&& t) {
data = std::forward<T>(t);
operations = { (OperationsToTypeErase::template invoker<ContainedType>)... };
return *this;
}
};
operator->*:
template<class...Ops, class F, class Sig,
// SFINAE filter that an op matches:
std::enable_if_t< std::disjunction< std::is_same<Ops, any_method<F,Sig>>... >{}, int> = 0
>
auto operator->*( super_any<Ops...>& a, any_method<F,Sig> f) {
auto fptr = std::get<typename any_method<F,Sig>::invoker_type*>(a.operations);
return [fptr,f, &a](auto&&... args) mutable {
return fptr(f, a.data, std::forward<decltype(args)>(args)...);
};
}
Usage:
#include <iostream>
auto print = make_any_method<void(std::ostream&)>(
[](auto&& self, auto&& os){ os << self; }
);
using printable_any = super_any<decltype(print)>;
printable_any bob = 7; // sets up the printing data attached to the any
int main() {
(bob->*print)(std::cout); // prints 7
bob = 3.14159;
(bob->*print)(std::cout); // prints 3.14159
}
Live
Given this code:
template< class C >
void foo( C const& o ) { o.nosuch(); }
struct Base {};
void foo( Base const& ) {}
struct Derived: Base {};
auto main() -> int
{
Derived d;
foo( d ); // !Invokes template.
}
… I want the call to invoke the overload defined for Base, without having to define an overload or template specialization for Derived.
The goal is to be able to apply foo to all kinds of objects, not just Base (and Derived) objects, with a generic implementation for most kinds of objects.
It would also be nice if an answer explained exactly how overload resolution works in this case, versus how it works with a Derived overload defined.
In the code where this problem manifested, foo above is a function template n_items defined as follows:
template< class Type >
auto n_items( Type const& o )
-> size_t
{ return o.size(); }
template< size_t n >
auto n_items( std::bitset<n> const& o )
-> size_t
{ return o.count(); } // Corresponds to std::set<int>::size()
template< class Type, size_t n >
constexpr
auto n_items( Raw_array_of_<n, Type> const& a )
-> size_t
{ return static_size( a ); }
And the intent is that this should be a catch-all for types that don't define their own n_items overloads.
And for a base and derived class, it should be enough that the base class defines a custom n_items; it would be very redundant to have to define it for every derived class.
How overload resolution works
First we do name lookup and template type deduction. For foo(d), this gives us two options:
foo(Derived const&), with [C = Derived]
foo(Base const&)
These are our viable candidates.
Then we determine which one of the overloads is the best viable candidate. This is done first by looking at conversion sequences. In this case, (1) is an Exact Match whereas (2) involves a derived-to-base conversion which has Conversion rank (see this table). Since that ranks worse, candidate (1) is preferred and is thus deemed the best viable candidate.
How to do what you really want
The simplest way is to simply add a third overload for Derived:
foo(Derived const&)
Here, both (1) and (3) would be equivalent in terms of conversion sequence. But functions that aren't templates are preferred to functions that are templates (think of it as picking the most specific option), so (3) would be selected.
But you don't want to do that. So the options are either: make (1) not work for Derived or make (2) work for Derived too in a way that's preferred.
Disable the general template
We can use SFINAE to simply exclude anything derived from Base:
template <class T, class = std::enable_if_t<!std::is_convertible<T*, Base*>::value>
void foo(T const& ); // new (1)
void foo(Base const& ); // same (2)
Now, (1) is no longer a viable candidate for the match, so (2) is trivally preferred.
Redo the overloads so that the Base is preferred
Taking a tip out of Xeo's book, we can restructure the overloads thusly:
template <int I> struct choice : choice<I + 1> { };
template <> struct choice<10> { };
struct otherwise { otherwise(...) {} };
template <class T> void foo(T const& val) { foo_impl(val, choice<0>{}); }
And now we can prefer those types derived from Base:
template <class T, class = std::enable_if_t<std::is_convertible<T*, Base*>::value>>
void foo_impl(T const& val, choice<0> ); // new (2)
template <class T>
void foo_impl(T const& val, otherwise ); // new (1)
This changes the mechanics of how overload resolution works and is worth going through into the separate cases.
Calling foo(d) means we're calling foo_impl(d, choice<0> ) and gives us two viable candidates:
foo_impl(Derived const&, choice<0> ), with [T = Derived]
foo_impl(Derived const&, otherwise ), with [T = Derived]
Here, the first argument is identical, but for the second argument, the first overload is an Exact Match while the second argument requires a Conversion via a variadic argument. otherwise will always be the word choice as a result, so the first overload is preferred. Exactly what we want.
Calling foo(not_a_base), on the other hand, would only give us one viable candidate:
foo_impl(NotABase const&, otherwise ), with [T = NotABase]
The other one was removed due to the deduction failure. So this is trivially the best viable candidate, and again we get exactly what we want.
For your specific question, I'd write something like:
template <class T>
size_t n_items(T const& cont) { return n_items(cont, choice<0>{}); }
with:
// has count?
template <class T>
auto n_items(T const& cont, choice<0> ) -> decltype(cont.count()) {
return cont.count();
}
// else, has size?
template <class T>
auto n_items(T const& cont, choice<1> ) -> decltype(cont.size()) {
return cont.size();
}
// else, use static_size
template <class T>
size_t n_items(T const& cont, otherwise )
return static_size(cont);
}
Put all the non-template functions in some namespace:
namespace Foo {
void foo( Base const& ) {}
}
Then define the function template thusly:
template <typename C>
auto foo_aux(C const& c, int) -> decltype(Foo::foo(c)) {
return Foo::foo(c);}
template <typename C>
void foo_aux(C const& c, ...) {
c.nosuch();}
template <typename C>
void foo(C const& c) {foo_aux(c, 0);}
Demo. This will call the general overload if and only if none of the non-template overloads match (or are ambiguous).
The main technical problem is that a general function template foo (say), instantiated for Derived argument, is a better match than the overload for Base argument.
Brute force solution (redundancy).
One simple possible solution is to require overloads of all the relevant functions for every class derived from a Base.
However, this breaks the DRY rule, Don't Repeat Yourself.
Needless redundancy in code leads to maintenance problems.
Namespace for SFINAE.
Another possible solution, suggested by Columbo, is to “put all the non-template functions in some [specific single] namespace”, which allows a general function template to use SFINAE to detect if a relevant overload exists.
This solution avoids the redundancy, and as I recall it is the kind of solution used for boost::intrusive_ptr, that client code must specialize the functionality in some specific namespace.
However, since it imposes a restriction on client code it feels not perfect to me.
Extra argument to direct overload resolution.
A third possible solution, suggested by Barry, is to let the base class implementation have an associated overload, or just a differently named function, that has an extra argument that serves to direct the overload resolution, and that is called by by a general function template.
This is technically a solution but IMO it's not clean: the client code invocations don't match the overloads that must be provided.
And so again there is a potential maintenance problem.
Invoking the function template from a simple converting overload.
Johannes Schaub suggested a fourth possible solution, which allows clean, simple client code, namely to let an ordinary overload call the function template, but where that general implementation overload has a formal argument type that
introduces a user defined conversion, which makes this a worse match than direct per-class overloads, and
has a templated constructor that picks up the actual argument type.
Johannes' original ingenious in-comment idea assumed a fixed function result type and exactly one argument. Generalizing that short idea description to arbitrary result type was not entirely trivial for me because that result type can depend on the actual argument, and one tends to first of all try to automate things like that. Likewise, generalizing to arbitrary number of arguments, with the first one acting as a this argument, was not 100% trivial for me. Johannes would no doubt have no problems doing this, and probably in a more neat way than I could do it. Anyway, my code:
#include <functional>
#include <utility>
//-------------------------------------- General machinery:
template< class Impl_func, class Result, class... Args >
class Invoker_of_
{
private:
std::function< auto(Args...) -> Result > f_;
public:
auto operator()( Args... args ) const -> Result { return f_( args... ); }
template< class Type >
Invoker_of_( Type& o )
: f_(
[&]( Args... args )
-> Result
{ return Impl_func{}.operator()( o, args... ); }
)
{}
};
//-------------------------------------- General template 1 (foo):
struct Foo_impl
{
template< class Type >
auto operator()( Type& o )
-> int
{ return o.foomethod(); }
};
auto foo( Invoker_of_<Foo_impl, int> const invoker )
-> int
{ return invoker(); }
//-------------------------------------- General template 2 (bar):
struct Bar_impl
{
template< class Type >
auto operator()( Type& o, int const arg1 )
-> int
{ return o.barmethod( arg1 ); }
};
auto bar( Invoker_of_<Bar_impl, int, int> const invoker, int const arg1 )
-> int
{ return invoker( arg1 ); }
//--------------------------------------- Usage examples:
struct Base {};
auto foo( Base const& ) -> int { return 101;}
auto bar( Base const&, int x ) -> int { return x + 2; }
struct Derived: Base {};
struct Other
{
auto foomethod() -> int { return 201; }
auto barmethod( int const x ) -> int { return x + 2; }
};
//--------------------------------------- Test driver:
#include <iostream>
using namespace std;
auto main() -> int
{
Derived d;
int const r1 = foo( d ); // OK, invokes non-template overload.
int const r2 = bar( d, 100 ); // OK, invokes non-template overload.
cout << r1 << " " << r2 << endl;
Other o;
int const r3 = foo( o ); // OK, invokes the general template.
int const r4 = bar( o, 200 ); // OK, invokes the general template.
cout << r3 << " " << r4 << endl;
}
I have not attempted to support rvalue reference arguments.
Output:
101 102
201 202
A variation Johannes Schaub's suggestion, which appears to yield the most clean usage, written up as code for the example at the start:
//-------------------------------------- Machinery:
template< class Type >
auto foo_impl( Type& o ) -> int { return o.method(); }
struct Invoker_of_foo_impl
{
int result;
template< class Type >
Invoker_of_foo_impl( Type& o ): result( foo_impl( o ) ) {}
};
auto foo( Invoker_of_foo_impl const invoker ) -> int { return invoker.result; }
//--------------------------------------- Usage:
struct Base {};
auto foo( Base const& ) -> int { return 6*7;}
struct Derived: Base {};
struct Other { auto method() -> int { return 0b101010; } };
auto main() -> int
{
Derived d;
foo( d ); // OK, invokes non-template.
Other o;
foo( o ); // OK, invokes template
}
I'm not sure I got the problem, but can't you use simply SFINAE and the is_base_of trait?
Using them, catch all functions are automatically excluded when the function resolution takes place for a class that is derived from Base and the best match is the non-template one.
Moreover, such a solution looks to me as far simpler than all the other... That's why I'm pretty sure that I didn't get the problem!! :-)
Anyway, it follows a working example:
#include<type_traits>
#include<iostream>
struct Base {};
auto foo( Base const& ) -> int {return 101;}
auto bar( Base const&, int x ) -> int {return x + 2; }
template<class T, typename = typename std::enable_if<not std::is_base_of<Base, T>::value>::type>
auto foo(T & t) -> int {
return t.foomethod();
}
template<class T, typename = typename std::enable_if<not std::is_base_of<Base, T>::value>::type>
auto bar(T & t, int i) -> int {
return t.barmethod(i);
}
struct Derived: Base {};
struct Other
{
auto foomethod() -> int { return 201; }
auto barmethod( int const x ) -> int { return x + 2; }
};
#include <iostream>
using namespace std;
auto main() -> int
{
Derived d;
int const r1 = foo( d ); // Invokes the Base arg overload.
int const r2 = bar( d, 100 ); // Invokes the Base arg overload.
cout << r1 << " " << r2 << endl;
Other o;
int const r3 = foo( o ); // Invokes the general implementation.
int const r4 = bar( o, 200 ); // Invokes the general implementation.
cout << r3 << " " << r4 << endl;
}
Let me know if I've misunderstood the problem, in that case I'm dropping the answer.
GOAL:
I would like to achieve type-safe dynamic polymorphism (i.e. run-time dispatch of a function call) on unrelated types - i.e. on types which do not have a common base class. It seems to me that this is achievable, or at least theoretically sound. I will try to define my problem more formally.
PROBLEM DEFINITION:
Given the following:
two or more unrelated types A1, ..., An, each of which has a method called f, possibly with different signatures, but with the same return type R; and
a boost::variant<A1*, ..., An*> object v (or whatever other type of variant) which can and must assume at any time one value of any of those types;
My goal is to write instructions conceptually equivalent to v.f(arg_1, ..., arg_m); that would get dispatched at run-time to function Ai::f if the actual type of the value contained in v is Ai. If the call arguments are not compatible with the formal parameters of each function Ai, the compiler should raise an error.
Of course I do not need to stick to the syntax v.f(arg_1, ..., arg_m): for instance, something like call(v, f, ...) is also acceptable.
I tried to achieve this in C++, but so far I have failed to come up with a good solution (I do have a bunch of bad ones). Below I clarify what I mean by "good solution".
CONSTRAINTS:
A good solution is anything that lets me mimic the v.f(...) idiom, e.g. call_on_variant(v, f, ...);, and satisfies the following constraints:
does not require any sort of separate declaration for each function f that must be called this way (e.g. ENABLE_CALL_ON_VARIANT(f)) or for any list of unrelated types A1, ..., An that can be treated polymorphically (e.g. ENABLE_VARIANT_CALL(A1, ..., An)) somewhere else in the code, especially on global scope;
does not require to explicitly name the types of the input arguments when doing the call (e.g. call_on_variant<int, double, string>(v, f, ...)). Naming the return type is OK, so for instance call_on_variant<void>(v, f, ...) is acceptable.
Follows a demonstrative example that hopefully clarifies my wish and requirements.
EXAMPLE:
struct A1 { void f(int, double, string) { cout << "A"; } };
struct A2 { void f(int, double, string) { cout << "B"; } };
struct A3 { void f(int, double, string) { cout << "C"; } };
using V = boost::variant<A1, A2, A3>;
// Do not want anything like the following here:
// ENABLE_VARIANT_CALL(foo, <whatever>)
int main()
{
A a;
B b;
C c;
V v = &a;
call_on_variant(v, f, 42, 3.14, "hello");
// Do not want anything like the following here:
// call_on_variant<int, double, string>(v, f, 42, 3.14, "hello");
V v = &b;
call_on_variant(v, f, 42, 3.14, "hello");
V v = &c;
call_on_variant(v, f, 42, 3.14, "hello");
}
The output of this program should be: ABC.
BEST (FAILED) ATTEMPT:
The closest I got to the desired solution is this macro:
#define call_on_variant(R, v, f, ...) \
[&] () -> R { \
struct caller : public boost::static_visitor<void> \
{ \
template<typename T> \
R operator () (T* pObj) \
{ \
pObj->f(__VA_ARGS__); \
} \
}; \
caller c; \
return v.apply_visitor(c); \
}();
Which would work perfectly, if only template members were allowed in local classes (see this question). Does anybody have an idea how to fix this, or suggest an alternative approach?
Some time has passed, C++14 is being finalized, and compilers are adding support for new features, like generic lambdas.
Generic lambdas, together with the machinery shown below, allow achieving the desired (dynamic) polymorphism with unrelated classes:
#include <boost/variant.hpp>
template<typename R, typename F>
class delegating_visitor : public boost::static_visitor<R>
{
public:
delegating_visitor(F&& f) : _f(std::forward<F>(f)) { }
template<typename T>
R operator () (T x) { return _f(x); }
private:
F _f;
};
template<typename R, typename F>
auto make_visitor(F&& f)
{
using visitor_type = delegating_visitor<R, std::remove_reference_t<F>>;
return visitor_type(std::forward<F>(f));
}
template<typename R, typename V, typename F>
auto vcall(V&& vt, F&& f)
{
auto v = make_visitor<R>(std::forward<F>(f));
return vt.apply_visitor(v);
}
#define call_on_variant(val, fxn_expr) \
vcall<int>(val, [] (auto x) { return x-> fxn_expr; });
Let's put this into practice. Supposing to have the following two unrelated classes:
#include <iostream>
#include <string>
struct A
{
int foo(int i, double d, std::string s) const
{
std::cout << "A::foo(" << i << ", " << d << ", " << s << ")";
return 1;
}
};
struct B
{
int foo(int i, double d, std::string s) const
{
std::cout << "B::foo(" << i << ", " << d << ", " << s << ")";
return 2;
}
};
It is possible to invoke foo() polymorphically this way:
int main()
{
A a;
B b;
boost::variant<A*, B*> v = &a;
auto res1 = call_on_variant(v, foo(42, 3.14, "Hello"));
std::cout << std::endl<< res1 << std::endl;
v = &b;
auto res2 = call_on_variant(v, foo(1337, 6.28, "World"));
std::cout << std::endl<< res2 << std::endl;
}
And the output is, as expected:
A::foo(42, 3.14, Hello)
1
B::foo(1337, 6.28, World)
2
The program has been tested on VC12 with November 2013's CTP. Unfortunately, I do not know of any online compiler that supports generic lambdas, so I cannot post a live example.
OK, here's a wild shot:
template <typename R, typename ...Args>
struct visitor : boost::static_visitor<R>
{
template <typename T>
R operator()(T & x)
{
return tuple_unpack(x, t); // this needs a bit of code
}
visitor(Args const &... args) : t(args...) { }
private:
std::tuple<Args...> t;
};
template <typename R, typename Var, typename ...Args>
R call_on_variant(Var & var, Args const &... args)
{
return boost::apply_visitor(visitor<R, Args...>(args...), var);
}
Usage:
R result = call_on_variant<R>(my_var, 12, "Hello", true);
I've hidden a certain amount of work you need for calling a function by unpacking a tuple, but I believe this has been done elsewhere on SO.
Also, if you need to store references rather than copies of the arguments, this can possibly be done, but needs more care. (You can have a tuple of references. But you have to think about whether you also want to allow temporary objects.)
Unfortunately, this cannot be done in C++ (yet - see the conclusions). Follows a proof.
CONSIDERATION 1: [on the need of templates]
In order to determine the correct member function Ai::f to be invoked at run-time when the expression call_on_variant(v, f, ...) is met (or any equivalent form of it), it is necessary, given the variant object v, to retrieve the type Ai of the value being held by v. Doing so necessarily requires the definition of at least one (class or function) template.
The reason for this is that no matter how this is done, what is needed is to iterate over all the types the variant can hold (the type list is exposed as boost::variant<...>::types, check whether the variant is holding a value of that type (through boost::get<>), and (if so) retrieve that value as the pointer through which the member function invocation must be performed (internally, this is also what boost::apply_visitor<> does).
For each single type in the list, this can be done this way:
using types = boost::variant<A1*, ..., An*>::types;
mpl::at_c<types, I>::type* ppObj = (get<mpl::at_c<types, I>::type>(&var));
if (ppObj != NULL)
{
(*ppObj)->f(...);
}
Where I is a compile-time constant. Unfortunately, C++ does not allow for a static for idiom that would allow a sequence of such snippets to be generated by the compiler based on a compile-time for loop. Instead, template meta-programming techniques must be used, such as:
mpl::for_each<types>(F());
where F is a functor with a template call operator. Directly or indirectly, at least one class or function template needs to be defined, since the lack of static for forces the programmer to code the routine that must be repeated for each type generically.
CONSIDERATION 2: [on the need of locality]
One of the constraints for the desired solution (requirement 1 of the section "CONSTRAINTS" in the question's text) is that it shall not be necessary to add global declarations or any other declaration at any other scope than the one where the function call is being done. Therefore, no matter whether macro expansion or template meta-programming is involved, what needs to be done must be done in the place where the function call occurs.
This is problematic, because "CONSIDERATION 1" above has proved that it is needed to define at least one template to carry out the task. The problem is that C++ does not allow templates to be defined at local scope. This is true of class templates and function templates, and there is no way to overcome this restriction. Per §14/2:
"A template-declaration can appear only as a namespace scope or class scope declaration"
Thus, the generic routines we have to define in order to do the job must be defined elsewhere than at call site, and must be instantiated at call-site with proper arguments.
CONSIDERATION 3: [on function names]
Since the call_on_variant() macro (or any equivalent construct) must be able to handle any possible function f, the name of f must be passed in as an argument to our template-based, type resolving machinery. It is important to stress that only the name of the function shall be passed, because the particular function Ai::f that needs to be invoked must be determined by the template machinery.
However, names cannot be template arguments, because they do not belong to the type system.
CONCLUSION:
The combination of the three considerations above proves that this problem cannot be solved in C++ as of today. It requires either the possibility of using names as template arguments or the possibility of defining local templates. While the first thing is undesirable at least, the second one might make sense, but it is not being taken into consideration by the standardization committee. However, one exception is likely to be admitted.
FUTURE OPPORTUNITIES:
Generic lambdas, which are being strongly pushed to get into the next C++ standard, are in fact local classes with a template call operator.
Thus, even though the macro I posted at the end of the question's text will still not work, an alternative approach seems viable (with some tweaking required for handling return types):
// Helper template for type resolution
template<typename F, typename V>
struct extractor
{
extractor(F f, V& v) : _f(f), _v(v) { }
template<typename T>
void operator () (T pObj)
{
T* ppObj = get<T>(&_v));
if (ppObj != NULL)
{
_f(*ppObj);
return;
}
}
F _f;
V& _v;
};
// v is an object of type boost::variant<A1*, ..., An*>;
// f is the name of the function to be invoked;
// The remaining arguments are the call arguments.
#define call_on_variant(v, f, ...) \
using types = decltype(v)::types; \
auto lam = [&] (auto pObj) \
{ \
(*pObj)->f(__VA_ARGS__); \
}; \
extractor<decltype(lam), decltype(v)>(); \
mpl::for_each<types>(ex);
FINAL REMARKS:
This is an interesting case of type-safe call that is (sadly) not supported by C++. This paper by Mat Marcus, Jaakko Jarvi, and Sean Parent seems to show that dynamic polymorphism on unrelated types is crucial to achieve an important (in my opinion, fundamental and unavoidable) paradigm shift in programming.
I once solved this by simulating .NET delegates:
template<typename T>
class Delegate
{
//static_assert(false, "T must be a function type");
};
template<typename ReturnType>
class Delegate<ReturnType()>
{
private:
class HelperBase
{
public:
HelperBase()
{
}
virtual ~HelperBase()
{
}
virtual ReturnType operator()() const = 0;
virtual bool operator==(const HelperBase& hb) const = 0;
virtual HelperBase* Clone() const = 0;
};
template<typename Class>
class Helper : public HelperBase
{
private:
Class* m_pObject;
ReturnType(Class::*m_pMethod)();
public:
Helper(Class* pObject, ReturnType(Class::*pMethod)()) : m_pObject(pObject), m_pMethod(pMethod)
{
}
virtual ~Helper()
{
}
virtual ReturnType operator()() const
{
return (m_pObject->*m_pMethod)();
}
virtual bool operator==(const HelperBase& hb) const
{
const Helper& h = static_cast<const Helper&>(hb);
return m_pObject == h.m_pObject && m_pMethod == h.m_pMethod;
}
virtual HelperBase* Clone() const
{
return new Helper(*this);
}
};
HelperBase* m_pHelperBase;
public:
template<typename Class>
Delegate(Class* pObject, ReturnType(Class::*pMethod)())
{
m_pHelperBase = new Helper<Class>(pObject, pMethod);
}
Delegate(const Delegate& d)
{
m_pHelperBase = d.m_pHelperBase->Clone();
}
Delegate(Delegate&& d)
{
m_pHelperBase = d.m_pHelperBase;
d.m_pHelperBase = nullptr;
}
~Delegate()
{
delete m_pHelperBase;
}
Delegate& operator=(const Delegate& d)
{
if (this != &d)
{
delete m_pHelperBase;
m_pHelperBase = d.m_pHelperBase->Clone();
}
return *this;
}
Delegate& operator=(Delegate&& d)
{
if (this != &d)
{
delete m_pHelperBase;
m_pHelperBase = d.m_pHelperBase;
d.m_pHelperBase = nullptr;
}
return *this;
}
ReturnType operator()() const
{
(*m_pHelperBase)();
}
bool operator==(const Delegate& d) const
{
return *m_pHelperBase == *d.m_pHelperBase;
}
bool operator!=(const Delegate& d) const
{
return !(*this == d);
}
};
You can use it much like .NET delegates:
class A
{
public:
void M() { ... }
};
class B
{
public:
void M() { ... }
};
A a;
B b;
Delegate<void()> d = Delegate<void()>(&a, &A::M);
d(); // calls A::M
d = Delegate<void()>(&b, &B::M);
d(); // calls B::M
This works with methods that have no arguments. If you can use C++11, you can modify it to use variadic templates to handle any number of parameters. Without C++11, you need to add more Delegate specializations to handle specific numbers of parameters:
template<typename ReturnType, typename Arg1>
class Delegate<ReturnType(Arg1)>
{
...
};
template<typename ReturnType, typename Arg1, typename Arg2>
class Delegate<ReturnType(Arg1, Arg2)>
{
...
};
With this Delegate class you can also emulate .NET events, which are based on delegates.