|
Re: Metaprogramming on Steroids: Overloaded Type Operators
|
Posted: Sep 26, 2005 11:02 AM
|
|
> Now as far as units go, check out > http://boost-consulting.com/mplbook/metafunctions.html as > per Indranil's suggestion. This pretty much makes the case > for me for simpler syntax for type manipulation. > > As far as how the type operator overload declarations > might go: I am thinking that cm * m maps to > _meta_star<cm, m>::type .
Ah, get you now. :) I've been thinking of this, too (no, I'm not saying it exists in ML ;) ): compile-time infix operators.
Presently, in C++, we have run-time prefix and infix operators (and postincrement as the "odd" postfix one), such as:
++i; // Prefix i+j; // Infix i++; // Postfix
Also, we have prefix compile-time expressions/metafunctions, such as:
mpl::add<a,b>::type // "a" and "b" are types or typedefs (typedefs being our "compile-time variables")
However, as you highlight, we have neither:
1) Operator overloading on types/compile-time expressions, and therefore neither do we have 2) Infix operations on types
I've played some with an idea about this, where you - as a rather big "hack", could indeed do compile-time infix computation (even including lambda (!)). This was also inspired by Alexey Gurtovoy's "round lambda" idea that he posted sometime on the Boost list, as well as some "wacky" ideas I got from discussions with Paul Mensonides (one of the authors of the Boost.Preprocessor library - the other being Vesa Karvonen).
The following is the complete, working code (requiring Boost, of course):
--- Start ---
#include <iostream> #include <boost/bind/placeholders.hpp> #include <boost/mpl/plus.hpp> #include <boost/mpl/multiplies.hpp> #include <boost/mpl/lambda.hpp> #include <boost/mpl/apply.hpp> #include <boost/mpl/int.hpp>
using namespace boost;
template<int N> struct size { char array[N]; };
size<1> operator+(arg<1>, arg<2>); // _1 + _2 size<2> operator*(size<1>, arg<1>); // (_1 + _2) * _1
template<int> struct expr;
template<> struct expr<1> : mpl::lambda<mpl::plus<mpl::_1, mpl::_2> >::type {};
template<> struct expr<2> : mpl::lambda<mpl::multiplies<mpl::plus<mpl::_1, mpl::_2>, mpl::_1> >::type {};
// Use:
int main() { typedef expr<sizeof(_1 + _2)> exprA; typedef expr<sizeof((_1 + _2) * _1)> exprB;
std::cout << mpl::apply<exprA, mpl::int_<1>, mpl::int_<2> >::type::value << '\n'; // 1 + 2 = 3 std::cout << mpl::apply<exprB, mpl::int_<10>, mpl::int_<20> >::type::value << '\n'; // (10 + 20) * 10 = 300 }
--- End ---
The infix operations I talk about are the definitions of "exprA" and "exprB". "_1" and "_2" are "placeholders", for the actual values that get substituted later (in the calls to mpl::apply).
In other words, it computes the result of the lambda expression "_1 + _2", etc. at compile-time (!). The result is a type, with a nested "value" member, so it does in effect allow you to do infix type computations. The "_1" and "_2" are values (instantiations of types) needed to drive the overload resolution, and you get the resulting type via a sizeof-to-type mapping (with typeof or type inferencing, you might do even more clever stuff).
It's mostly for fun, :) and hardly usable in its present form, as you need to manually encode any expression in the prefix form (see the expr<1> and expr<2> specialisations).
> Does this make sense?
Indeed. :)
Regards,
Terje
|
|