C
clintonmead
/*
(I've commented this code so it should compile just by copying/pasting the message. You'll also need boost.)
Lets say we want to create a boost::variant, with a few options. For example, we could be creating a filestream, but our options might be a dummy filestream, stdout or a real file stream. But for simplicity in this case, let's assume the options are 'int', 'std::string' and 'char'. Lets also assumewe have functions which return appropriate values of each type, like so:
*/
#include <string>
int f() { return 42; }
std::string g() { return "The meaning of life"; }
char h() { return '!'; }
/*
Now we'd like to make a variant like this:
auto x = make_variant(n, f(), g(), h());
For n = 0, x = 42, for n = 1, x = "The meaning of life" and for x = 2, n = '!'.
Also, this way, the type of the boost::variant is inferred.
However, the problem with this is that all of f(), g() and h() are evaluated. Not much of a problem in this case, but if f(), g() or h() were complicated this could be an issue.
If we want to delay evaluation, we might do this:
auto x = make_variant(n, []{return f();}, []{return g();}, []{return h();});
This looks a bit ugly now, but we can now write 'make_variant' like below:
*/
#include <boost/variant.hpp>
#include <functional>
#include <stdexcept>
template <class RETURN_T>
RETURN_T make_variant_worker(int n)
{
throw std::runtime_error("Invalid selection");
}
template <class RETURN_T, class FIRSTARG, class... ARGS>
RETURN_T make_variant_worker(int n, const FIRSTARG& first_arg, const ARGS&.... args)
{
return n == 0 ? first_arg() : make_variant_worker<RETURN_T>(n - 1, args...);
}
template <class... ARGS>
auto make_variant(int n, const ARGS&... args) -> boost::variant<decltype(args())...>
{
return make_variant_worker<boost::variant<decltype(args())...>>(n, args....);
}
/*
We can then use the 'make_variant' function as follows:
*/
int main()
{
int n;
std::cin >> n;
auto x = make_variant(n, []{return f();}, []{return g();}, []{return h();});
std::cout << x << std::endl;
}
/*
However, the call to 'make_variant' is a bit ugly. What I'd like to be ableto do is somewhere in the 'make_variant' function put an attribute on the function arguments that implicitly wraps all the arguments with "[] { return arg; }", i.e. delays their evaluation. Hence, I could call:
make_variant(n, f(), g(), h());
Without evaluating f(), g() or h() unnecessarily.
This isn't without precedent in C++, the non-overridden "&&", "||" and "?:"all don't necessarily evaluate all of their arguments. Such a feature would allow one not only to write a 'make_variant' function without splatteringlambdas all throughout the code, but also create short circuit operators for "&&" and "||" (say, for three value logic), and functions like "if_then_else" that behave like their builtin equivalents.
I'm not sure how to make this a formal proposal, even if I did I wouldn't know how to express the details. However, I think it would be fairly simple to implement because it's effectively just syntactic sugar, particularly when the call is occurring internally in one compilation unit. I'm not sure if I'm reinventing the wheel here, if this can already be done in C++11 I'd be interested to see how, otherwise if someone wants to take this up that'smore experienced than me with these things I'd be happy with that too.
Regards,
Clinton Mead
*/
(I've commented this code so it should compile just by copying/pasting the message. You'll also need boost.)
Lets say we want to create a boost::variant, with a few options. For example, we could be creating a filestream, but our options might be a dummy filestream, stdout or a real file stream. But for simplicity in this case, let's assume the options are 'int', 'std::string' and 'char'. Lets also assumewe have functions which return appropriate values of each type, like so:
*/
#include <string>
int f() { return 42; }
std::string g() { return "The meaning of life"; }
char h() { return '!'; }
/*
Now we'd like to make a variant like this:
auto x = make_variant(n, f(), g(), h());
For n = 0, x = 42, for n = 1, x = "The meaning of life" and for x = 2, n = '!'.
Also, this way, the type of the boost::variant is inferred.
However, the problem with this is that all of f(), g() and h() are evaluated. Not much of a problem in this case, but if f(), g() or h() were complicated this could be an issue.
If we want to delay evaluation, we might do this:
auto x = make_variant(n, []{return f();}, []{return g();}, []{return h();});
This looks a bit ugly now, but we can now write 'make_variant' like below:
*/
#include <boost/variant.hpp>
#include <functional>
#include <stdexcept>
template <class RETURN_T>
RETURN_T make_variant_worker(int n)
{
throw std::runtime_error("Invalid selection");
}
template <class RETURN_T, class FIRSTARG, class... ARGS>
RETURN_T make_variant_worker(int n, const FIRSTARG& first_arg, const ARGS&.... args)
{
return n == 0 ? first_arg() : make_variant_worker<RETURN_T>(n - 1, args...);
}
template <class... ARGS>
auto make_variant(int n, const ARGS&... args) -> boost::variant<decltype(args())...>
{
return make_variant_worker<boost::variant<decltype(args())...>>(n, args....);
}
/*
We can then use the 'make_variant' function as follows:
*/
int main()
{
int n;
std::cin >> n;
auto x = make_variant(n, []{return f();}, []{return g();}, []{return h();});
std::cout << x << std::endl;
}
/*
However, the call to 'make_variant' is a bit ugly. What I'd like to be ableto do is somewhere in the 'make_variant' function put an attribute on the function arguments that implicitly wraps all the arguments with "[] { return arg; }", i.e. delays their evaluation. Hence, I could call:
make_variant(n, f(), g(), h());
Without evaluating f(), g() or h() unnecessarily.
This isn't without precedent in C++, the non-overridden "&&", "||" and "?:"all don't necessarily evaluate all of their arguments. Such a feature would allow one not only to write a 'make_variant' function without splatteringlambdas all throughout the code, but also create short circuit operators for "&&" and "||" (say, for three value logic), and functions like "if_then_else" that behave like their builtin equivalents.
I'm not sure how to make this a formal proposal, even if I did I wouldn't know how to express the details. However, I think it would be fairly simple to implement because it's effectively just syntactic sugar, particularly when the call is occurring internally in one compilation unit. I'm not sure if I'm reinventing the wheel here, if this can already be done in C++11 I'd be interested to see how, otherwise if someone wants to take this up that'smore experienced than me with these things I'd be happy with that too.
Regards,
Clinton Mead
*/