On 8/9/2010 5:39 PM, Manjunath Kudlur wrote:
>> Actually, I think your users will be forced to do: (module->*func)(...)
>> because of the precedence. That's pretty ugly. But I see where you're
>> going. Your scheme could be made to work as you describe, I think.
>
> Yes, you are right, I had forgotten about the precedence.
>
>> I'm left wondering why you want this, though. In what way is it better
>> than just defining some mutually recursive functions?
>
> How would you do that? Do you have some other mechanism in mind?
I was just talking about plain ol' vanilla C-style functions. Kickin' it
old school.
> To be
> clear, I want the ability to "name" some proto expressions, and freely
> "call" these expressions in other expressions in a mutually recursive
> manner, just like in the example I have shown. My use case is to
> provide the user with a set of data-parallel primitives that he can
> freely compose to form bigger functions.
Now I have a very fuzzy picture, but it's still not clear yet. Anyway,
it seems like an interesting programming challenge so I took a crack at
it myself. I got distracted so didn't finish it, but here's what I came
up with so far.
The biggest problem I saw was that the syntax "module.func(a,b,c)" can
mean two different things. When building the symbol table, it should
create an expression template. But outside of symbol-table-building, it
should actually evaluate the function, using the symbol table to
statically look up the functions to evaluate. This is tricky! But I
think it can be accomplished by using two different domains. I am too
tired to explain what I mean, so I'll just post what I have so far and
hope that you'll get the idea.
--
Eric Niebler
BoostPro Computing
http://www.boostpro.com
#include <boost/proto/proto.hpp>
#include <boost/proto/proto_typeof.hpp>
#include <boost/fusion/container/vector.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <vector>
#include <iostream>
using namespace std;
namespace mpl=boost::mpl;
namespace proto=boost::proto;
using proto::_;
template<typename>
struct arg {};
proto::terminal<arg<mpl::int_<0> > >::type const _1 = {{}};
proto::terminal<arg<mpl::int_<1> > >::type const _2 = {{}};
proto::terminal<arg<mpl::int_<2> > >::type const _3 = {{}};
template<typename>
struct func {};
struct _map {};
proto::terminal<_map>::type const map = {{}};
struct _symbol_table_tag {};
//// Eval would be used to actually evaluate a function
//// invocation of module.func(x,y,z), as opposed to
//// simply creating the symbol table. This is incomplete.
//struct Eval
// : proto::or_<
// proto::when<
// proto::terminal< arg<_> >
// , // TODO look up argument in state tuple
// >
// , proto::when<
// proto::function<
// proto::member<
// proto::terminal<_symbol_table_tag> // the module
// , proto::terminal< func<_> > // the function
// >
// , proto::vararg<Eval>
// >
// , // TODO downcast the module to access the symbol
// // table. Look up the function in the symbol table.
// // Then evaluate the function.
// >
// , proto::otherwise<
// proto::_default<Eval>
// >
// >
//{};
#define MK_FUNC_MEMBER(R, DATA, I, ELEM) ((func<mpl::int_<I> >, ELEM))
// Define the symbols. Probably should be parameterized on a
// domain instead of (or in addition to) the symbol table.
#define DECLARE_SYMBOL_TABLE(NAME, SYMBOLS)
\
template<typename Module>
\
struct NAME
\
{
\
typedef Module module_type;
\
module_type derived() { return *static_cast<module_type *>(this); }
\
module_type const derived() const { return *static_cast<module_type
const *>(this); } \
BOOST_PROTO_BASIC_EXTENDS(
\
proto::terminal<_symbol_table_tag>::type
\
, NAME<Module>
\
, proto::default_domain
\
)
\
BOOST_PROTO_EXTENDS_MEMBERS(BOOST_PP_SEQ_FOR_EACH_I(MK_FUNC_MEMBER, ~,
SYMBOLS)) \
static NAME<Module> const self;
\
};
\
template<typename Module>
\
NAME<Module> const NAME<Module>::self = {}
// Given a set of symbols and some declarations involving the
// symbols, build a symbol table.
#define DECLARE_MODULE(NAME, SYMBOLS, DECLS)
\
struct NAME : SYMBOLS<NAME>
\
{
\
typedef BOOST_TYPEOF(proto::deep_copy(DECLS)) table_type;
\
table_type const table;
\
NAME()
\
: table(proto::deep_copy(DECLS))
\
{}
\
}
// This declares a bunch of symbols that we can later use
// in a module to define function declarations
DECLARE_SYMBOL_TABLE(
symbol_table
, (vadd)
(negmul)
(det_inverse)
(vmul)
(fib)
(precondition)
);
// This declares a module containing function declarations.
DECLARE_MODULE(
module
, symbol_table
, (
self.vadd = _1 + _2
, self.negmul = -self.vmul(_1, _2)
, self.det_inverse = 1 / (_1 * _3 - _2 * _2)
, self.vmul = _1 * _2
, self.fib = self.fib(_1 - 1) + self.fib(_1 - 2)
, self.precondition = self.vmul(map(self.det_inverse, _1, _2, _3),
_1)
)
);
int main()
{
module mod;
std::vector<int> a, b, c;
// Right now, this just creates an expression template
// representing the expression. We'd need to hook this
// to get it to actually evaluate the expression.
mod.precondition(a, b, c);
// This displays a very small tree because the decls
// have been type-erased. All that type information is
// available statically, so that's not a problem.
proto::display_expr(mod.precondition(a, b, c));
}
_______________________________________________
proto mailing list
[email protected]
http://lists.boost.org/mailman/listinfo.cgi/proto