note: this question was briefly marked as a duplicate of this, but it is not an exact duplicate since I am asking about std::optionals specifically. Still a good question to read if you care about general case.
Assume I have nested optionals, something like this(dumb toy example):
struct Person{
const std::string first_name;
const std::optional<std::string> middle_name;
const std::string last_name;
};
struct Form{
std::optional<Person> person;
};
and this spammy function:
void PrintMiddleName(const std::optional<Form> form){
if (form.has_value() && form->person.has_value() && form->person->middle_name.has_value()) {
std::cout << *(*(*form).person).middle_name << std::endl;
} else {
std::cout << "<none>" << std::endl;
}
}
What would be the best way to flatten this optional check?
I have made something like this, it is not variadic, but I do not care that much about that(I can add one more level(overload with membr3) if really necessary, and everything beyond that is terrible code anyway).
template<typename T, typename M>
auto flatten_opt(const std::optional<T> opt, M membr){
if (opt.has_value() && (opt.value().*membr).has_value()){
return std::optional{*((*opt).*membr)};
}
return decltype(std::optional{*((*opt).*membr)}){};
}
template<typename T, typename M1, typename M2>
auto ret_val_helper(){
// better code would use declval here since T might not be
// default constructible.
T t;
M1 m1;
M2 m2;
return ((t.*m1).value().*m2).value();
}
template<typename T, typename M1, typename M2>
std::optional<decltype(ret_val_helper<T, M1, M2>())> flatten_opt(const std::optional<T> opt, M1 membr1, M2 membr2){
if (opt.has_value() && (opt.value().*membr1).has_value()){
const auto& deref1 = *((*opt).*membr1);
if ((deref1.*membr2).has_value()) {
return std::optional{*(deref1.*membr2)};
}
}
return {};
}
void PrintMiddleName2(const std::optional<Form> form){
auto flat = flatten_opt(form, &Form::person, &Person::middle_name);
if (flat) {
std::cout << *flat;
}
else {
std::cout << "<none>" << std::endl;
}
}
godbolt
notes:
I do not want to switch away from std::optional to some better optional.
I do not care that much about perf, unless I return a pointer I must make copy(unless arg is temporary) since std::optional does not support references.
I do not care about flatten_has_value function(although it is useful), since if there is a way to nicely flatten the nested optionals there is also a way to write that function.
I know my code looks like it works, but it is quite ugly, so I am wondering if there is a nicer solution.
The operation you're looking for is called the monadic bind operation, and is sometimes spelled and_then (as it is in P0798 and Rust).
You're taking an optional<T> and a function T -> optional<U> and want to get back an optional<U>. In this case the function is a pointer to data member, but it really does behave as a function in this sense. &Form::person takes a Form and gives back an optional<Person>.
You should write this in a way that is agnostic to the kind of function. The fact that it's specifically a pointer to member data isn't really important here, and maybe tomorrow you'll want a pointer to member function or even a free function. So that's:
template <typename T,
typename F,
typename R = std::remove_cvref_t<std::invoke_result_t<F, T>>,
typename U = mp_first<R>>
requires SpecializationOf<R, std::optional>
constexpr auto and_then(optional<T> o, F f) -> optional<U>
{
if (o) {
return std::invoke(f, *o);
} else {
return std::nullopt;
}
}
This is one of the many kinds of function declarations that are just miserable to write in C++, even with concepts. I'll leave it as an exercise to properly add references into there. I choose to specifically write it as -> optional<U> rather than -> R because I think it's important for readability that you can see that it does, in fact, return some kind of optional.
Now, the question is how do we chain this to multiple functions. Haskell uses >>= for monadic bind, but in C++ that has the wrong association (o >>= f >>= g would evaluate f >>= g first and require parentheses). So the next closest chose of operator would be >> (which means something different in Haskell, but we're not Haskell, so it's okay). Or you could implement this borrowing the | model that Ranges does.
So we'd either end up syntactically with:
auto flat = form >> &Form::person >> &Person::middle_name;
or
auto flat = form | and_then(&Form::person)
| and_then(&Person::middle_name);
A different way to compose multiple monadic binds together is an operation that Haskell spells >=>, which is called Kleisli composition. In this case, it takes a function T -> optional<U> and a function U -> optional<V> and produces a function T -> optional<V>. This is something that is exceedingly annoying to write constraints for so I'm just going to skip it, and it would look something like this (using the Haskell operator spelling):
template <typename F, typename G>
constexpr auto operator>=>(F f, G g) {
return [=]<typename T>(T t){
using R1 = std::remove_cvref_t<std::invoke_result_t<F, T>>;
static_assert(SpecializationOf<R1, std::optional>);
using R2 = std:remove_cvref_t<std::invoke_result_t<G, mp_first<R1>>>;
static_assert(SpecializationOf<R2, std::optional>);
if (auto o = std::invoke(f, t)) {
return std::invoke(g, *o);
} else {
// can't return nullopt here, have to specify the type
return R2();
}
};
}
And then you could write (or at least you could if >=> were an operator you could use):
auto flat = form | and_then(&Form::person >=> &Person::middle_name);
Because the result of >=> is now a function that takes a Form and returns an optional<string>.
Let's look at what the optimal form of a flatten function would look like. By "optimal" in this case, I mean the smallest presentation.
Even in the optimal case, at the point of performing the flatten operation, you would need to provide:
The optional<T> object to flatten.
The flatten operation function name.
A list of names, in order, to be indirected from at each flattening step.
Your code is very close to optimal. The only issue is that each name in the "list of names" must contain the typename of the member you're accessing at that level, which is something that hypothetically could be computed using knowledge of T.
C++ has no mechanism to do any better than this. If you want to access a member of an object, you must provide the type of that object. If you want to indirectly do this, C++ allows member pointers, but getting such a pointer requires knowing the type of the object at the point when the member is extracted. Even offsetof gymnastics would require using the type name when you're getting the offset.
Reflection would allow for something better, as you could pass compile-time strings that static reflection could use to fetch member pointers from the type currently in use. But C++20 has no such feature.
You've got a lot of helper functions for something that is fundamentally a chainable operation. And C++ has things for chains: operators. So I'd probably (ab)use operator* for this.
For your specific case, all you need is
template<class class_t, class member_t>
std::optional<std::remove_cv_t<member_t>> operator*(
const std::optional<class_t>& opt,
const std::optional<member_t> class_t::*member)
{
if (opt.has_value()) return opt.value().*member;
else return {};
}
void PrintMiddleName2(const std::optional<Form> form){
auto middle = form * &Form::person * &Person::middle_name;
if (middle) {
std::cout << *middle;
}
else {
std::cout << "<none>" << std::endl;
}
}
But in reality you'd probably also want variants for non-optional members, getter methods, and arbitrary transforms, which I've listed here, though I'm not 100% certain they all compile properly.
//data member
template<class class_t, class member_t>
std::optional<std::remove_cv_t<member_t>> operator*(const std::optional<class_t>& opt, const std::optional<member_t> class_t::*member) {
if (opt.has_value()) return opt.value().*member;
else return {};
}
template<class class_t, class member_t>
std::optional<std::remove_cv_t<member_t>> operator*(const std::optional<class_t>& opt, const member_t class_t::*member) {
if (opt.has_value()) return {opt.value().*member};
else return {};
}
//member function
template<class class_t, class return_t>
std::optional<std::remove_cv_t<return_t>> operator*(const std::optional<class_t>& opt, std::optional<return_t>(class_t::*member)()) {
if (opt.has_value()) return opt.value().*member();
else return {};
}
template<class class_t, class return_t>
std::optional<std::remove_cv_t<return_t>> operator*(const std::optional<class_t>& opt, return_t(class_t::*member)()) {
if (opt.has_value()) return {opt.value().*member()};
else return {};
}
//arbitrary function
template<class class_t, class return_t, class arg_t>
std::optional<std::remove_cv_t<return_t>> operator*(const std::optional<class_t>& opt, std::optional<return_t>(*transform)(arg_t&&)) {
if (opt.has_value()) return transform(opt.value());
else return {};
}
template<class class_t, class return_t, class arg_t>
std::optional<std::remove_cv_t<return_t>> operator*(const std::optional<class_t>& opt, return_t(*transform)(arg_t&&)) {
if (opt.has_value()) return {transform(opt.value())};
else return {};
}
http://coliru.stacked-crooked.com/a/26aa7a62f38bbd89
Related
My colleague provided me with a "small quiz", that he made his students solve once. It seems that my feeble mind is just unable to comprehend all the beauty of the modern C++ capabilities.
Subj:
Implementing a join function, accepting arbitrary functors and returning another functor, which behaves as any of them. Ex.:
{
auto result = std::visit(custom::join(
[](std::string const& s) { return "it's a string"; },
[](std::pair<int, int> const& p) { return "it's a pair"; }
), var);
assert(result == "it's a string");
var = std::make_pair(10, 20);
auto lvalue_lambda = [](std::string const& s) { return "it's a string"; };
result = std::visit(custom::join(
lvalue_lambda,
[](std::pair<int, int> const& p) { return "it's a pair"; }
), var);
assert(result == "it's a pair");
}
Okay, after a bit of thinking I got the idea, that std::variant means "one of the listed" as far as it is a "type-safe union", so I'll need a tuple. Tried something like that:
namespace custom
{
template<typename ...Functors>
class ResultFunctor
{
public:
ResultFunctor(Functors&&... funcs)
: m_funcs(std::make_tuple(std::move(funcs)...))
{}
template<typename ...Params>
auto operator()(Params... params) // that's where I got stuck
{
// return std::get<void(Params...)>(m_funcs)(params...); // No, the return type spoils this idea
return std::get<0>(m_funcs)(params...); // Now I need to choose the correct functor
}
private:
std::tuple<Functors...> m_funcs;
};
template<typename ...Functors>
ResultFunctor<Functors...> join(Functors&&... funcs)
{
return ResultFunctor(std::move(funcs)...);
}
}
If it were only for functors with void return type, I'd easily get the desired element of tuple. But there seems to be no way to determine it, return type cannot be deduced from given parameters (obviously).
Another idea was to use some SFINAE trick to choose the correct operator()() version, but this way or another I'll have to "run through" all the tuple item (which is nasty, but still can be googled) and then check if that item is suitable, based on the given parameters pack.
Well, that's where I took a pause to think it over thoroughly. If anyone (who deals better with all that variadic stuff) would have any ideas, I'd be really grateful.
This has a really simple solution that doesn't involve SFINAE or template metaprogramming (just regular templates).
The first step is to write a functor which represents the joint overload set. This is easy to achieve with inheritance, and because all the functors used as inputs must have different types, we don't have to do anything fancy.
// This represents overload set
template<class F1, class F2>
struct Joint : public F1, public F2 {
using F1::operator();
using F2::operator();
};
For user convenience, we can add a deduction guide:
template<class F1, class F2>
Joint(F1, F2) -> Joint<F1, F2>;
Because Joint is an aggregate type in C++17 and above, we don't have to provide a constructor because we can use aggregate initialization:
// This code magically works
auto result = std::visit(Joint{
[](std::string const& s) { return "it's a string"; },
[](std::pair<int, int> const& p) { return "it's a pair"; }
}, var);
Writing a custom::join function is equally simple:
template<class F1, class F2>
auto join(F1&& f1, F2&& f2) {
return Joint { std::forward<F1>(f1), std::forward<F2>(f2) };
}
Now that we have the base case, we can generalize it pretty easily:
template<class F, class F2, class... Fs>
auto join(F&& f, F2&& f2, Fs&&... fs) {
return Joint{
std::forward<F>(f),
join(std::forward<F2>(f2), std::forward<Fs>(fs)...)
};
}
Addressing potential criticisms
Why not define a constructor for Joint? Aggregate initialization is the most efficient form of initialization, because when you don't define a constructor the compiler is capable of assigning values in-place without having to copy or move them.
Why use multiple inheritance? If we rely on SFINAE, that increases compiletimes, increases code complexity, and it doesn't work as expected in some cases. With SFINAE, you have to check each member of the overload set to see if it's a fit. In some cases, due to implicit conversion, a worse overload is chosen because it was a match.
By using inheritance, we can use the languages' built-in pattern-matching for function calls.
Why add deduction guides? They make the code cleaner, and in this case they work exactly as expected: arguments are stored by value
namespace custom {
template<class...Fs>
struct overloaded : Fs... {
using Fs::operator()...;
};
template<class...Fs>
overloaded(Fs...)->overloaded<Fs...>;
template<class F>
F&& as_obj( F&& f ){ return std::forward<F>(f); }
template<class R, class...Args>
auto as_obj( R(*f)(Args...) {
struct helper {
R(*f)(Args...);
R operator()(Args...args) const { return f(std::forward<Args>(args)...); }
};
return helper{f};
}
template<class...Fs>
auto join( Fs&&...fs ){
return overloaded{as_obj(std::forward<Fs>(fs))...};
}
}
I added as a bonus support for non-overloaded function pointers.
I have been struggling with how to make a minimal verifiable example of this but I cannot think of how to do it. I have two Expected Types that I am trying to add together. The expected can be a data type such as int, double, etc. Or it can be an exception. In theory, I should be able to add the data type with an exception and it will be able to run the program just fine, when I ask for the value. It should return the exception without crashing the program.
Whenever I try to run this program I get hundreds of lines of error messages that I don't even know where to begin, the one error I see is no matching function for call to 'operator+(double&,double&) I do not know if this is correct either, because I want to add two Expected's together, I do not want to add the types themselves. In the end, I want to add an Expected, and get returned and Expected.
I am really stuck here, I was told that my apply function has been implemented incorrectly but I honestly cannot see why that is the case. What am I doing wrong?
#include <stdexcept>
#include <exception>
#include <functional>
#include <variant>
template<typename T>
class Expected
{
public:
Expected(T t) : state(t), valid(true){}
Expected(std::exception_ptr e) : state(e), valid(false){}
Expected(std::exception e) : state(std::make_exception_ptr(e)), valid(false){}
T value() const
{
if(valid) return std::get<T>(state);
std::rethrow_exception(std::get<std::exception_ptr>(state));
}
bool isValid()
{
if(valid) return true;
return false;
}
template<typename U>
Expected<U> apply(std::function<U(T)> f)
{
if(!valid) return std::get<std::exception_ptr>(state);
try
{
return f(std::get<T>(state));
}
catch(...)
{
return std::current_exception();
}
}
private:
std::variant<T, std::exception_ptr> state;
bool valid;
};
template<typename T>
std::ostream& operator<< (std::ostream& o, Expected<T> e)
{
try
{
o << e.value();
}
catch(std::exception &a)
{
o << a.what();
}
catch(...)
{
o << "Unexpected Error";
}
return o;
}
template<typename T, typename V>
auto operator+(Expected<T> t, Expected<V> v)
{
return t.apply([&](T myT){return operator+(myT,v);});
}
template<typename T, typename V>
auto operator+(Expected<T> t, V v)
{
return t.apply([&](T myT){return operator+(myT,v);});
}
template<typename T, typename V>
auto operator+(V v, Expected<T> t)
{
return t.apply([&](T myT){return operator+(v,myT);});
}
int main()
{
Expected<int> a = 1;
Expected<int> b = 2;
std::cout << a + b << std::endl;
}
Here's a reduced example of the first problem:
int main() {
operator+(1, 2); // error
}
You cannot call builtin operators by name. You can only call them by operator. Moreover, just using the operator is easier to read anyway - so just do that. Use +. (Also, in one part of your code, you're using op instead of what presumably should be operator+).
Here's a reduced example of the second problem:
template <typename T>
struct X {
template<typename U>
U apply(std::function<U(T)> f);
};
int main() {
X<int>{}.apply([](int ){return 2.0;}); // error
}
Basically, deducing a std::function is almost always wrong. This lambda is not a std::function, it is not a std::function<double(int)>. What you want to do, almost always, is deduce an arbitrary callable, and then use a metafunction to determine the result:
template <typename T>
struct X {
template <typename F, typename U = std::invoke_result_t<F&, T>>
U apply(F f);
};
First off: In general, if you're getting too many errors, simplify your test case by leaving out some code until you're down to one problem in the code (that could still be reported as multiple errors, though). Either you will understand that error, or you will have a concrete thing to ask about. In this case, this would mean starting with just one overload of operator +, with no operator <<.
Now to your code: there are multiple issues. First off, why are you calling operator+ (a, b) instead of the more logical a + b? This actually causes the error you've mentioned explicitly in the question, because there's not operator+ function taking two doubles: the built-in operator + is not a function and cannot be called as such.
The second problem is that the template parameter U of apply cannot be deduced from lambda expressions, because the type of a lambda expression is not std::function. Which means you have to provide an explicit template argument for it:
return t.template apply<decltype(std::declval<T>() + v)>([&](T myT){return myT + v;});
This change needs to happen in all 3 overloads of operator + (I assume the unknown op in the 3rd overload is a weird typo and should actually also be calling +).
Notice the need for the template keyword before apply, because it's used in a dependent context.
Note that Barry's answer provides a better approach to this by getting rid of std::function altogether.
Finally, you're missing #include <iostream>.
With all these changes in place, your code [works].
If I put a T into an std::any, I can get it with any_cast<T>(my_any). But does the standard (= C++17, in the ballot phase at the moment) include a function like any_cast<T>(optional<any> oa) which returns nullopt if oa is nullopt and std::any_cast<T>(oa.value()) otherwise? Or something along those lines?
Edit: Since people seem to be suggesting implementations, I'll also list what I use for now:
/* using magic here to select between boost/std::experimental/std versions */
template<typename T>
inline const optional<T> any_cast(const optional<any>& operand)
{
return operand ?
optional<T>(any_cast<T>(operand.value())) :
optional<T>(nullopt);
}
There is no mention of anything like that in the std::optional proposal or in the std::any proposal.
I suppose it would be trivial to implement using a continuation function, since the return type is different depending on the state of the optional object:
template <typename T, typename TOptional, typename TF>
void any_cast_or_nullopt(TOptional&& o, TF&& f)
{
if(!o) return;
f(std::any_cast<T>(*o));
}
Add static_assert and/or SFINAE where appropriate to constrain the function. The value *o should also be forwarded depending on o's value category. Example usage:
int out = -1;
std::optional<std::any> x;
x = 10;
any_cast_or_nullopt<int>(x, [&out](int value)
{
out = value;
});
assert(out == 10);
If std::optional had a bind (or and_then) member function (that is, a function on optional<T> which takes a T -> optional<U> and either invokes it or returns nullopt), then that's what you'd be looking for:
std::optional<std::any>> oa;
optional<T> opt_t = oa.bind([](std::any& v) -> std::optional<T> {
if (T* t = std::any_cast<T>(&v)) {
return *t;
}
else {
return std::nullopt;
}
});
or, if you really want to directly invoke any_cast<T> and deal with throwing, map:
optional<T> opt_t = oa.map([](std::any& v) {
return std::any_cast<T>(v);
});
std::optional has no continuation functions though, so you'd have to write those as non-member functions.
I'm trying to programming in C++ a framework where the user can indicates a set of functions inside its program where he wants to apply a memoization strategy.
So let's suppose that we have 5 functions in our program f1...f5 and we want to avoid the (expensive) re-computation for the functions f1 and f3 if we already called them with the same input. Notice that each function can have different return and argument types.
I found this solution for the problem, but you can use only double and int.
MY SOLUTION
Ok I wrote this solution for my problem, but I don't know if it's efficient, typesafe or can be written in any more elegant way.
template <typename ReturnType, typename... Args>
function<ReturnType(Args...)> memoize(function<ReturnType(Args...)> func)
{
return ([=](Args... args) mutable {
static map<tuple<Args...>, ReturnType> cache;
tuple<Args...> t(args...);
auto result = cache.insert(make_pair(t, ReturnType{}));
if (result.second) {
// insertion succeeded so the value wasn't cached already
result.first->second = func(args...);
}
return result.first->second;
});
}
struct MultiMemoizator
{
map<string, boost::any> multiCache;
template <typename ReturnType, typename... Args>
void addFunction(string name, function < ReturnType(Args...)> func) {
function < ReturnType(Args...)> cachedFunc = memoize(func);
boost::any anyCachedFunc = cachedFunc;
auto result = multiCache.insert(pair<string, boost::any>(name,anyCachedFunc));
if (!result.second)
cout << "ERROR: key " + name + " was already inserted" << endl;
}
template <typename ReturnType, typename... Args>
ReturnType callFunction(string name, Args... args) {
auto it = multiCache.find(name);
if (it == multiCache.end())
throw KeyNotFound(name);
boost::any anyCachedFunc = it->second;
function < ReturnType(Args...)> cachedFunc = boost::any_cast<function<ReturnType(Args...)>> (anyCachedFunc);
return cachedFunc(args...);
}
};
And this is a possible main:
int main()
{
function<int(int)> intFun = [](int i) {return ++i; };
function<string(string)> stringFun = [](string s) {
return "Hello "+s;
};
MultiMemoizator mem;
mem.addFunction("intFun",intFun);
mem.addFunction("stringFun", stringFun);
try
{
cout << mem.callFunction<int, int>("intFun", 1)<<endl;//print 2
cout << mem.callFunction<string, string>("stringFun", " World!") << endl;//print Hello World!
cout << mem.callFunction<string, string>("TrumpIsADickHead", " World!") << endl;//KeyNotFound thrown
}
catch (boost::bad_any_cast e)
{
cout << "Bad function calling: "<<e.what()<<endl;
return 1;
}
catch (KeyNotFound e)
{
cout << e.what()<<endl;
return 1;
}
}
How about something like this:
template <typename result_t, typename... args_t>
class Memoizer
{
public:
typedef result_t (*function_t)(args_t...);
Memoizer(function_t func) : m_func(func) {}
result_t operator() (args_t... args)
{
auto args_tuple = make_tuple(args...);
auto it = m_results.find(args_tuple);
if (it != m_results.end())
return it->second;
result_t result = m_func(args...);
m_results.insert(make_pair(args_tuple, result));
return result;
}
protected:
function_t m_func;
map<tuple<args_t...>, result_t> m_results;
};
Usage is like this:
// could create make_memoizer like make_tuple to eliminate the template arguments
Memoizer<double, double> memo(fabs);
cout << memo(-123.456);
cout << memo(-123.456); // not recomputed
It's pretty hard to guess at how you're planning to use the functions, with or without memoisation, but for the container-of-various-function<>s aspect you just need a common base class:
#include <iostream>
#include <vector>
#include <functional>
struct Any_Function
{
virtual ~Any_Function() {}
};
template <typename Ret, typename... Args>
struct Function : Any_Function, std::function<Ret(Args...)>
{
template <typename T>
Function(T& f)
: std::function<Ret(Args...)>(f)
{ }
};
int main()
{
std::vector<Any_Function*> fun_vect;
auto* p = new Function<int, double, double, int> { [](double i, double j, int z) {
return int(i + j + z);
} };
fun_vect.push_back(p);
}
The problem with this is how to make it type-safe. Look at this code:
MultiMemoizator mm;
std::string name = "identity";
mm.addFunction(name, identity);
auto result = mm.callFunction(name, 1);
Is the last line correct? Does callFunction have the right number of parameters with the right types? And what is the return type?
The compiler has no way to know that: it has no way of understanding that name is "identity" and even if it did, no way to associate that with the type of the function. And this is not specific to C++, any statically-typed language is going to have the same problem.
One solution (which is basically the one given in Tony D's answer) is to tell the compiler the function signature when you call the function. And if you say it wrong, a runtime error occurs. That could look something like this (you only need to explicitly specify the return type, since the number and type of parameters is inferred):
auto result = mm.callFunction<int>(name, 1);
But this is inelegant and error-prone.
Depending on your exact requirements, what might work better is to use "smart" keys, instead of strings: the key has the function signature embedded in its type, so you don't have to worry about specifying it correctly. That could look something like:
Key<int(int)> identityKey;
mm.addFunction(identityKey, identity);
auto result = mm.callFunction(identityKey, 1);
This way, the types are checked at compile time (both for addFunction and callFunction), which should give you exactly what you want.
I haven't actually implemented this in C++, but I don't see any reason why it should be hard or impossible. Especially since doing something very similar in C# is simple.
you can use vector of functions with signature like void someFunction(void *r, ...) where r is a pointer to result and ... is variadic argument list. Warning: unpacking argument list is really inconvenient and looks more like a hack.
At first glance, how about defining a type that has template arguments that differ for each function, i.e.:
template <class RetType, class ArgType>
class AbstractFunction {
//etc.
}
have the AbstractFunction take a function pointer to the functions f1-f5 with template specializations different for each function. You can then have a generic run_memoized() function, either as a member function of AbstractFunction or a templated function that takes an AbstractFunction as an argument and maintains a memo as it runs it.
The hardest part will be if the functions f1-f5 have more than one argument, in which case you'll need to do some funky things with arglists as template parameters but I think C++14 has some features that might make this possible. An alternative is to rewrite f1-f5 so that they all take a single struct as an argument rather than multiple arguments.
EDIT: Having seen your problem 1, the problem you're running into is that you want to have a data structure whose values are memoized functions, each of which could have different arguments.
I, personally, would solve this just by making the data structure use void* to represent the individual memoized functions, and then in the callFunction() method use an unsafe type cast from void* to the templated MemoizedFunction type you need (you may need to allocate MemoizedFunctions with the "new" operator so that you can convert them to and from void*s.)
If the lack of type safety here irks you, good for you, in that case it may be a reasonable option just to make hand-written helper methods for each of f1-f5 and have callFunction() dispatch one of those functions based on the input string. This will let you use compile-time type checking.
EDIT #2: If you are going to use this approach, you need to change the API for callFunction() slightly so that callFunction has template args matching the return and argument types of the function, for example:
int result = callFunction<int, arglist(double, float)>("double_and_float_to_int", 3.5, 4);
and if the user of this API ever types the argument type or return types incorrectly when using callFunction... pray for their soul because things will explode in very ugly ways.
EDIT #3: You can to some extent do the type checking you need at runtime using std::type_info and storing the typeid() of the argument type and return type in your MemoizedFunction so that you can check whether the template arguments in callFunction() are correct before calling - so you can prevent the explosion above. But this will add a bit of overhead every time you call the function (you could wrap this in a IF_DEBUG_MODE macro to only add this overhead during testing and not in production.)
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.