Want feedback on C++ version of Python's explode operator

K

k04jg02

Python has a nifty operator that will take a container and pass its
elements as function parameters. In Python you can make a list like
so:

x = [1, 2, 3]

Then you can say:

f(*x)

Which is the same as f(x[0], x[1], x[2]). I thought it would be nice
to have the same functionality for C++. I've written an implementation
of it, that lets you write things like this:

int sum(int x, int y) { return x + y; }

boost::array<int, 2> operands = { 3, 4 };
boost::function<int(int, int)> Sum = sum;
explode(Sum, operands); // Same as writing sum(operands[0],
operands[1])

It makes heavy use of template and boost preprocessor magic, but it
seems to work. I'm looking for feedback on how to improve it (it looks
rather obfuscated). I'm still new to heavy template usage and so their
might be better ways to accomplish this. Right now it's limited to
working on containers that are of a type with a static_size member
defining their size (like boost::array has), but should be easily
expandable to working on builtin arrays, specified ranges, etc.

I've divided it up into three files:
1. bind_append.hpp -- Defines a wrapper around boost::bind that
assumes you want to bind a parameter to the end of the parameter list
and leave the rest of the parameters alone in normal order.
2. remove_arg.hpp -- A function trait that gives you the same function
type with one parameter removed.
3. explode.hpp -- implementation of explode()

// ===========bind_append.hpp=================
/*
Author: Joseph Garvin (2006)

bindappend is a wrapper around boost::bind that assumes you want to
append
1 argument to the current function object, immediately to the end of
the
argument list, and want to leave placeholders, in order, for the
rest. It's
less flexible than bind but combined with remove_arg is useful for
iteratively
applying parameters to a function. Example:

int sum(int x, int y, int z) { return x + y + z; }

bindappend(sum, 3) // Equivalent to bind(sum, 3, _1, 2)
*/

#ifndef BOOST_PP_IS_ITERATING

#ifndef INCLUDED_BIND_APPEND_H
#define INCLUDED_BIND_APPEND_H

#include <boost/preprocessor/repetition.hpp>
#include <boost/static_assert.hpp>
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/type_traits/function_traits.hpp>
#include <meta/remove_arg.hpp>

using namespace boost;

template<class FuncType, class ParamType, int remainingArity>
struct bindappend_impl
{
// Error! We have no remaining arity!
BOOST_STATIC_ASSERT ( sizeof ( ParamType ) == 0 );
};

#ifndef BIND_APPEND_MAX_SIZE
#define BIND_APPEND_MAX_SIZE 8
#endif

#define BOOST_PP_ITERATION_LIMITS (0, BIND_APPEND_MAX_SIZE)
#define BOOST_PP_FILENAME_1 <bind_append.hpp>
#include BOOST_PP_ITERATE()

template<class FuncType, class ParamType>
function<typename remove_arg<FuncType>::type>
bind_append(function<FuncType> func, ParamType param)
{
return bindappend_impl<FuncType, ParamType,
function_traits<FuncType>::arity - 1>::bind_append(func, param);
}

#endif // INCLUDED_BIND_APPEND_H

#else // BOOST_PP_IS_ITERATING

#define n BOOST_PP_ITERATION()

template<class FuncType, class ParamType>
struct bindappend_impl <FuncType, ParamType, n>
{
static function<typename remove_arg<FuncType>::type>
bind_append(function<FuncType> func, ParamType param);
};

template<class FuncType, class ParamType>
function<typename remove_arg<FuncType>::type>
bindappend_impl<FuncType, ParamType,
n>::bind_append(function<FuncType> func, ParamType param)
{
return bind(func, param BOOST_PP_COMMA_IF (n)
BOOST_PP_ENUM_SHIFTED_PARAMS (BOOST_PP_ADD(n, 1), _) );
}

#undef n

#endif // BOOST_PP_IS_ITERATING


// ===========remove_arg.hpp=================
/*
Author: Joseph Garvin (2006)

remove_arg is a compile time function that takes a function type and
returns
the same function type with one less argument. E.g. it transforms:

int(char, float, double)

To:

int(float, double)

Example Usage:

boost::function<remove_arg<FuncType> > foo = boost::bind_append(foo,
x);
*/

#ifndef BOOST_PP_IS_ITERATING

#ifndef INCLUDED_REMOVE_ARG_H
#define INCLUDED_REMOVE_ARG_H

#include <boost/preprocessor/repetition.hpp>
#include <boost/static_assert.hpp>
#include <boost/preprocessor/iteration/iterate.hpp>

template<typename R>
struct remove_arg
{
// Error! We have no parameters to strip!
BOOST_STATIC_ASSERT ( sizeof ( R ) == 0 );
};

#ifndef REMOVE_ARG_MAX_SIZE
#define REMOVE_ARG_MAX_SIZE 8
#endif

#define BOOST_PP_ITERATION_LIMITS (1, REMOVE_ARG_MAX_SIZE)
#define BOOST_PP_FILENAME_1 <remove_arg.hpp>
#include BOOST_PP_ITERATE()

#endif // INCLUDED_REMOVE_ARG_H

#else // BOOST_PP_IS_ITERATING

#define n BOOST_PP_ITERATION()

template <class R BOOST_PP_COMMA_IF ( n ) BOOST_PP_ENUM_PARAMS ( n,
class T ) >
struct remove_arg <R ( BOOST_PP_ENUM_PARAMS ( n,T ) ) >
{
typedef R type ( BOOST_PP_ENUM_SHIFTED_PARAMS (n, T ) );
};

#undef n

#endif // BOOST_PP_IS_ITERATING


// ===========explode.hpp=================
#include <boost/function.hpp>
#include <boost/type_traits/function_traits.hpp>
#include <boost/bind.hpp>
#include <boost/static_assert.hpp>

#include <meta/remove_arg.hpp>
#include <meta/bind_append.hpp>

template<class FuncSig, class FixedSizeSTLContainer, int arity>
class Explode_Helper
{
public:
static typename function_traits<FuncSig>::result_type
explode_helper(function<FuncSig> func, FixedSizeSTLContainer args);
};

template<class FuncSig, class FixedSizeSTLContainer, int arity>
typename function_traits<FuncSig>::result_type
Explode_Helper<FuncSig, FixedSizeSTLContainer,
arity>::explode_helper(function<FuncSig> func, FixedSizeSTLContainer
args)
{
function<typename remove_arg<FuncSig>::type > appliedFunc =
bind_append(func, args[FixedSizeSTLContainer::static_size - arity]);
return Explode_Helper<
typename remove_arg<FuncSig>::type,
FixedSizeSTLContainer,
function_traits<typename remove_arg<FuncSig>::type>::arity
>
::
explode_helper(appliedFunc, args);
}

template<class FuncSig, class FixedSizeSTLContainer>
class Explode_Helper<FuncSig, FixedSizeSTLContainer, 0>
{
public:
static typename function_traits<FuncSig>::result_type
explode_helper(function<FuncSig> func, FixedSizeSTLContainer args);
};

template<class FuncSig, class FixedSizeSTLContainer>
typename function_traits<FuncSig>::result_type
Explode_Helper<FuncSig, FixedSizeSTLContainer,
0>::explode_helper(function<FuncSig> func, FixedSizeSTLContainer args)
{
return func();
}

template<class FuncSig, class FixedSizeSTLContainer>
typename function_traits<FuncSig>::result_type
explode(function<FuncSig> func, FixedSizeSTLContainer args)
{
BOOST_STATIC_ASSERT ( function_traits<FuncSig>::arity ==
FixedSizeSTLContainer::static_size );

return Explode_Helper<FuncSig, FixedSizeSTLContainer,
function_traits<FuncSig>::arity>::explode_helper(func, args);
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top