Boost logo

Boost Users :

Subject: [Boost-users] Selecting function overloads with boost::overload()
From: Reza Jahanbakhshi (reza.jahanbakhshi_at_[hidden])
Date: 2016-03-23 07:46:28


First a little bit of background.
Recently I've been using boost.python and I noticed choosing a specific
function overload is kind of messy when defining class interfaces or free
functions. Consider the following functions and member functions:

void fn(int i, float f);
int fn(const std::string& str);

float car::method();
void car::method(int i, double d);

According to boost.python documentation we can add these function using
either of following constructs:

void (*fn1)(int, float) = fn;
int (*fn2)(const std::string&) = fn;
def("fn", fn1);
def("fn", fn2);

float (car::*method1)() = &car::method;
void (car::*method2)(int, double) = &car::method;
.def("method", method1)
.def("method", method2)

or to avoid those intermediate variables we can do it directly:

def("fn", (void (*)(int, float))fn);
def("fn", (int (*)(const std::string&))fn);

.def("method", (float(car::*)())&car::method)
.def("method", (void (car::*)(int, double))&car::method)

Which in my opinion is not readable enough. So I wrote a small utility to
make it a little bit more readable. For example:

def("fn", overload<void(int, float)>(fn));
def("fn", overload<int(const std::string&)(fn));

.def("method", overload<float()>(&car::method));
.def("method", overload<void(int, double)>(&car::method));

Notice how I gave the desired signature to the overload function and it
will choose that exact overload for me. It's not only limited to
boost.python and we can use it wherever we need to select a specific
overloaded function. For example:

auto f = overload<void(int, float)>(fn);

Of course it's just a syntactic sugar and you might not find it improving
anything at all but in my case it helped my code readability somehow.

Now my question. I pasted the whole thing here. As for non-member functions
I could implement it relying only on C++11 variadic templates. But for
member functions I couldn't find a way to do it without boost.preprocess
and using only C++11 variadic template. Here is the full code tested with
GCC 5.3.1 (std=C++11), Clang 3.7.0 (std=C++11) and MSVC2015

#include <type_traits>
#include <boost/mpl/vector.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/repetition.hpp>

namespace boost {

namespace detail {

template <typename SignatureType>
struct fn_signature;

template <typename ResultType, typename... ArgType>
struct fn_signature<ResultType(ArgType...)>
{
  using result_type = ResultType;
  using arg_type = boost::mpl::vector<ArgType...>;
  static const std::size_t arg_count = sizeof...(ArgType);
  using type = ResultType(*)(ArgType...);
};

template <typename ClassType, typename SignatureType>
struct memfn_signature;

template <typename ClassType, typename ResultType, typename... ArgType>
struct memfn_signature<ClassType, ResultType(ArgType...)>
{
  using type = ResultType(ClassType::*)(ArgType...);
};

} // namespace detail

template <typename SignatureType>
typename detail::fn_signature<SignatureType>::type overload(typename
detail::fn_signature<SignatureType>::type fn)
{
  return fn;
}

#define BOOST_OVERLOAD_MAX_ARGS_COUNT 5
#define BOOST_OVERLOAD_PP_METHOD_OVERLOAD_ARGS(Z, N, C)
BOOST_PP_COMMA_IF(N) typename
std::enable_if<detail::fn_signature<SignatureType>::arg_count == C,
typename boost::mpl::at_c<typename
detail::fn_signature<SignatureType>::arg_type, N>::type>::type
#define BOOST_OVERLOAD_PP_METHOD_OVERLOAD_IMPL(Z, N, _) \
template <typename SignatureType, typename ClassType> \
typename detail::memfn_signature<ClassType, SignatureType>::type
overload(typename
detail::fn_signature<SignatureType>::result_type(ClassType::*memfn)(BOOST_PP_REPEAT(N,
BOOST_OVERLOAD_PP_METHOD_OVERLOAD_ARGS, N))) \
{ \
  return memfn; \
}
BOOST_PP_REPEAT(BOOST_PP_INC(BOOST_OVERLOAD_MAX_ARGS_COUNT),
BOOST_OVERLOAD_PP_METHOD_OVERLOAD_IMPL, _)
#undef BOOST_OVERLOAD_PP_METHOD_OVERLOAD_IMPL
#undef BOOST_OVERLOAD_PP_METHOD_OVERLOAD_ARGS
#undef BOOST_OVERLOAD_MAX_ARGS_COUNT

} // namespace boost

The problem is I cannot find a way to force the overload resolution to pick
the specific overload and also automatically deduce the ClassType template
parameter at the same time. If the language let me to alias a variadic
template parameter then it would be possible to do it. For example I could
do this:

template <typename ClassType, typename ResultType, typename... ArgType>
struct memfn_signature<ClassType, ResultType(ArgType...)>
{
  using type = ResultType(ClassType::*)(ArgType...);
  using result_type = ResultType;
  *using arg_type = ArgType...;*
};

template <typename SignatureType, typename ClassType>
typename detail::memfn_signature<ClassType, SignatureType>::type
overload(typename detail::memfn_signature<SignatureType>::result_type
(ClassType::*memfn)(typename
detail::memfn_signature<SignatureType>::arg_type))
{
  return memfn;
}

Right now my preprocess implementation works with no problem but It's not a
clean implementation. So is there any other way that I can do this. I
appreciate your help.



Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net