How to implement Matt Calabrese's BOOST_AUTO_FUNCTION idea?

Viewed 162

In a talk from BoostCon 2011, Matt Calabrese gives the following hypothetical syntax:

template< class L, class R >
BOOST_AUTO_FUNCTION( operator -( L const& lhs, R const& rhs )
                   , if ( is_vector_udt< L > )
                        ( is_vector_udt< R > )
                   , try ( lhs + rhs )
                         ( -rhs )
                   , if typename ( L::value_type )
                   )
(
  return lhs + -rhs
)

The idea is that this declares a function template:

  1. named operator-,
  2. with arguments L const& lhs and R const& rhs,
  3. which does not participate in overload resolution unless is_vector_udt<L> and is_vector_udt<R> are true,
  4. which does not participate in overload resolution unless lhs + rhs and -rhs are valid expressions,
  5. which does not participate in overload resolution unless L::value_type is a valid type,
  6. whose body is return lhs + -rhs;, and
  7. with return type deduced from the given body;

using concepts-like syntax without actual language-level concepts (it's supposed to work in the C++11 we got, not the one we wanted).

I'm most interested in points 3, 4, and 5 in the list above. The proposed syntax repurposes words that would normally be keywords. For example, try here obviously does not refer to exception handling, but the macro would have to transform try(lhs+rhs)(-rhs) into something that could participate in SFINAE, such as sizeof((void)(lhs+rhs),(void)(-rhs),0), but only if it occurs inside an invocation of the BOOST_AUTO_FUNCTION macro. I'm not familiar with advanced preprocessing techniques so I can't figure out how this could be done.

Or maybe I misunderstood, and Calabrese wasn't actually claiming that this syntax was implementable (though that would be strange; I would think that he would have instead chosen to show some syntax that is implementable).

2 Answers

tl;dr:

  • As it is BOOST_AUTO_FUNCTION would have not been implementable in C++11

    • The SFINAE parts would have worked without a problem
    • The return-type deduction from arbitrary statements would have been impossible, due to lambdas not being allowed in unevaluated contexts
  • A slightly modified version of BOOST_AUTO_FUNCTION is possible, that doesn't deduce the return type from the expressions but rather another parameter.
    e.g.:

    template< class L, class R >
    BOOST_AUTO_FUNCTION( operator -( L const& lhs, R const& rhs ),
                       lhs + -rhs // <- this will be used to deduce the return type
                     , if ( is_vector_udt< L > )
                          ( is_vector_udt< R > )
                     , try ( lhs + rhs )
                           ( -rhs )
                     , if typename ( L::value_type )
                     )
    (
      return lhs + -rhs
    )
    
  • Here's a full implementation of how this modified BOOST_AUTO_FUNCTION would look like:

The rest of this post goes into the details of how to build BOOST_AUTO_FUNCTION in C++11 using macros without any libraries:


1. Parsing the conditions

Let's first start with a macro that can parse a single condition list from the BOOST_AUTO_FUNCTION macro (e.g. if (A)(B)(C) / try (A)(B) into something we can use in C++.

The basis for this is - as @chris metioned in the comments - concatenation. By prepending a fixed token we can convert if / try into macro names for further processing.

Such a PARSE_COND macro could look like this:

#define CONCAT_IMPL(a, b) a ## b
#define CONCAT(a,b) CONCAT_IMPL(a, b)

#define COND_if    <-IF->
#define COND_try   <-TRY->

#define PARSE_COND(expr) CONCAT(COND_,expr)

This already allows us to replace the leading if / try with anything we want.

Macro Expansion Examples: godbolt

PARSE_COND(if (A)(B))              =>      <-IF-> (A)(B)
PARSE_COND(try (A)(B))             =>      <-TRY-> (A)(B)
PARSE_COND(if typename (A)(B))     =>      <-IF-> typename (A)(B)

We can use this technique to substitute the if / try with a call to another sets of macros (HANDLE_IF / HANDLE_TRY) that will handle the next stage of processing: godbolt

#define COND_if    HANDLE_IF (
#define COND_try   HANDLE_TRY (

#define HANDLE_IF(expr) DOIF: expr
#define HANDLE_TRY(expr) DOTRY: expr 

#define PARSE_COND(expr) CONCAT(COND_,expr))
PARSE_COND(if (A)(B))              =>      DOIF: (A)(B)
PARSE_COND(try (A)(B))             =>      DOTRY: (A)(B)
PARSE_COND(if typename (A)(B))     =>      DOIF: typename (A)(B)

1.1 Handling multi-token identifiers like if typename

Now we need to deal with distinguishing a plain if from if typename.
Unfortunately we can't utilize the same approach as for if / try, because concatenation needs to result in a valid preprocessor token - and concatenating something to an opening parenthesis will always produce an invalid token => we'd get a preprocessor error.

e.g. something like this would work fine for if typename, but would cause an error with just if: godbolt

#define COND_IF_typename HANDLE_IF_TYPENAME ( 
#define COND_IF_(...) HANDLE_IF_IF ( (__VA_ARGS__)

#define HANDLE_IF(expr) CONCAT(COND_IF_, expr))

#define HANDLE_IF_TYPENAME(expr) DOIFTYPENAME: expr
#define HANDLE_IF_IF(expr) DOIF: expr
PARSE_COND(if typename (A)(B))     =>      DOIFTYPENAME: (A)(B)
PARSE_COND(if (A)(B))              =>      <compiler error>

So we need a way to detect if there are more tokens left to be parsed (e.g. typename) or if we've already reached the parenthesized conditions.

For this we'll have to use some macro-shenanigans - it is actually possible to check if an expression starts with something parenthesized or not by using function-like macros.

If the name of a function-like macro is followed by parenthesis it will expand, otherwise the name of the macro will be left untouched.

Example: godbolt

#define CHECK(...) EXPANDED!
#define EXPANSION_CHECK(expr) CHECK expr
EXPANSION_CHECK((A)(B))              =>      EXPANDED!(B)
EXPANSION_CHECK(typename (A)(B))     =>      CHECK typename (A)(B)

By using this property of function-like macros we can write a macro that can detect if there are more tokens in a given expression: godbolt

#define EXPAND(...) __VA_ARGS__
#define EMPTY()
#define DEFER(id) id EMPTY()

#define HAS_MORE_TOKENS(expr) EXPAND(DEFER(HAS_MORE_TOKENS_RESULT)(HAS_MORE_TOKENS_CHECK expr, 0, 1))
#define HAS_MORE_TOKENS_CHECK(...) ~,~
#define HAS_MORE_TOKENS_RESULT(a, b, c, ...) c
HAS_MORE_TOKENS(typename (A)(B))     =>      1
HAS_MORE_TOKENS((A)(B))              =>      0

Using this we can now handle any sequence of tokens - if there are more tokens we can use the CONCAT()-trick to further expand them, and if we've reached the parenthesized conditions we can stop and know which sequence of tokens we've read before.

Example: godbolt

#define CONCAT_IMPL(a, b) a ## b
#define CONCAT(a,b) CONCAT_IMPL(a, b)

#define EXPAND(...) __VA_ARGS__
#define EMPTY()
#define DEFER(id) id EMPTY()

#define HAS_MORE_TOKENS(expr) EXPAND(DEFER(HAS_MORE_TOKENS_RESULT)(HAS_MORE_TOKENS_CHECK expr, 0, 1))
#define HAS_MORE_TOKENS_CHECK(...) ~,~
#define HAS_MORE_TOKENS_RESULT(a, b, c, ...) c

#define IIF(condition, a, b) CONCAT(IIF_, condition)(a, b)
#define IIF_0(a, b) b
#define IIF_1(a, b) a

#define COND_if    HANDLE_IF (
#define COND_try   HANDLE_TRY (

#define COND_IF_typename HANDLE_IF_TYPENAME (

#define HANDLE_IF(expr) IIF(HAS_MORE_TOKENS(expr), HANDLE_IF_MORE, HANDLE_IF_IF)(expr)
#define HANDLE_IF_MORE(expr) CONCAT(COND_IF_,expr))

#define HANDLE_IF_TYPENAME(expr) DOIFTYPENAME: expr
#define HANDLE_IF_IF(expr) DOIF: expr
#define HANDLE_TRY(expr) DOTRY: expr 

#define PARSE_COND(expr) CONCAT(COND_,expr))
PARSE_COND(if (A)(B))              =>      DOIF: (A)(B)
PARSE_COND(try (A)(B))             =>      DOTRY: (A)(B)
PARSE_COND(if typename (A)(B))     =>      DOIFTYPENAME: (A)(B)
1.2 Building SFINAE expressions

Next we need to convert the actual expressions into valid C++ SFINAE code for the 3 different types of checks.

We'll inject the checks into the return-type, e.g.:

template<class L, class R>
auto operator+(L const&, R const&) -> decltype(<check A>, <check B>, <actual deduced return type>) {
  /* ... */
}
  • For if we can use std::enable_if:
    expr -> typename std::enable_if<expr::value>::type()
    This will result in void() if expr is true, and produce a substitution error if expr is false
  • For try we can just leave the expression as it is.
  • For if typename we can use std::declval:
    expr -> std::declval<typename expr>()
    We need to use std::declval because expr might not be default-constructible.

So with a small foreach macro we can now convert all 3 types of SFINAE-conditions into C++-Code: godbolt

#define SEQ_HEAD(seq) EXPAND(DEFER(SEQ_HEAD_IMPL)(SEQ_HEAD_EL seq))
#define SEQ_HEAD_EL(el) el,
#define SEQ_HEAD_IMPL(head, tail) head
#define SEQ_TAIL(seq) SEQ_TAIL_IMPL seq
#define SEQ_TAIL_IMPL(el)

#define FOR_EACH(func, seq) IIF(HAS_MORE_TOKENS(seq), FOR_EACH_END, FOR_EACH_0)(func, seq)
#define FOR_EACH_END(...)
#define FOR_EACH_0(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_1)(func, SEQ_TAIL(seq))
#define FOR_EACH_1(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_2)(func, SEQ_TAIL(seq))
#define FOR_EACH_2(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_3)(func, SEQ_TAIL(seq))
#define FOR_EACH_3(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_4)(func, SEQ_TAIL(seq))
#define FOR_EACH_4(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_END)(func, SEQ_TAIL(seq))

#define HANDLE_IF_TYPENAME(expr) FOR_EACH(HANDLE_IF_TYPENAME_IMPL, expr)
#define HANDLE_IF_TYPENAME_IMPL(expr) std::declval<typename expr>(),
#define HANDLE_IF_IF(expr) FOR_EACH(HANDLE_IF_IF_IMPL, expr)
#define HANDLE_IF_IF_IMPL(expr) typename std::enable_if<expr::value>::type(),
#define HANDLE_TRY(expr) FOR_EACH(HANDLE_TRY_IMPL, expr)
#define HANDLE_TRY_IMPL(expr) expr,
PARSE_COND(if (A)(B))              =>      typename std::enable_if<A::value>::type(), typename std::enable_if<B::value>::type(),
PARSE_COND(try (A)(B))             =>      A, B,
PARSE_COND(if typename (A)(B))     =>      std::declval<typename A>(), std::declval<typename B>(),

2. Building BOOST_AUTO_FUNCTION

Now that we can parse the conditions, we almost have everything we need to get an actual implementation of BOOST_AUTO_FUNCTION.

We just need another FOR_EACH implementation to loop over the different condition lists passed to BOOST_AUTO_FUNCTION, and a few more macros:

#define FOR_EACH_I(func, seq) IIF(HAS_MORE_TOKENS(seq), FOR_EACH_I_END, FOR_EACH_I_0)(func, seq)
#define FOR_EACH_I_END(...)
#define FOR_EACH_I_0(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_1)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_1(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_2)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_2(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_3)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_3(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_4)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_4(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_END)(func, SEQ_TAIL(seq))


#define TUP_SIZE(...) TUP_SIZE_IMPL(,##__VA_ARGS__,3,2,1,0)
#define TUP_SIZE_IMPL(a,b,c,d,e,...) e
#define TUP_TO_SEQ(...) CONCAT(TUP_TO_SEQ_, TUP_SIZE(__VA_ARGS__))(__VA_ARGS__)
#define TUP_TO_SEQ_0()
#define TUP_TO_SEQ_1(a) (a)
#define TUP_TO_SEQ_2(a,b) (a)(b)
#define TUP_TO_SEQ_3(a,b,c) (a)(b)(c)

#define HANDLE_CONDS(...) FOR_EACH_I(PARSE_COND, TUP_TO_SEQ(__VA_ARGS__))
#define INFER_RETURN_TYPE(...) ([&]() { __VA_ARGS__; })()
#define BUILD_FUNC_BODY(...) INFER_RETURN_TYPE(__VA_ARGS__)) { __VA_ARGS__; }
#define BOOST_AUTO_FUNCTION(signature, ...) auto signature -> decltype( HANDLE_CONDS(__VA_ARGS__) BUILD_FUNC_BODY

And that's already it - now we have implemented BOOST_AUTO_FUNCTION: godbolt

template< class L, class R >
BOOST_AUTO_FUNCTION( operator -( L const& lhs, R const& rhs )
                   , if ( is_vector_udt< L > )
                        ( is_vector_udt< R > )
                   , try ( lhs + rhs )
                         ( -rhs )
                   , if typename ( L::value_type )
                   )
(
  return lhs + -rhs
)
template< class L, class R >
auto operator -( L const& lhs, R const& rhs ) -> decltype(
    typename std::enable_if<is_vector_udt< L >::value>::type(),
    typename std::enable_if<is_vector_udt< R >::value>::type(),
    lhs + rhs,
    -rhs,
    std::declval<typename L::value_type>(),
    ([&]() { return lhs + -rhs; })()) // <- problem
{
    return lhs + -rhs;
}

There's a problem though - lambdas can't be used in unevaluated contexts (in C++20 they now can be used, but only without capture-clauses).
So this unfortunately doesn't compile as it is.

There's also no easy workaround for this (at least in C++11 - in C++14 we could use automatic return type deduction) - so unfortunately as it is BOOST_AUTO_FUNCTION would not have been implementable as-is in C++11.


3. Making it work

One way you could make BOOST_AUTO_FUNCTION viable in C++11 could be by removing the last feature from your list:

  1. with return type deduced from the given body;

If we e.g. add an additional parameter to BOOST_AUTO_FUNCTION specifically for return-type deduction it would work:

template< class L, class R >
BOOST_AUTO_FUNCTION( operator -( L const& lhs, R const& rhs ),
                     lhs + -rhs // <- this will be used for return-type deduction
                   , if ( is_vector_udt< L > )
                        ( is_vector_udt< R > )
                   , try ( lhs + rhs )
                         ( -rhs )
                   , if typename ( L::value_type )
                   )
(
  return lhs + -rhs
)

Then we just need to slightly modify our existing macros to make it work: godbolt

#define HANDLE_CONDS(...) FOR_EACH_I(PARSE_COND, TUP_TO_SEQ(__VA_ARGS__))
#define BUILD_FUNC_BODY(...) { __VA_ARGS__; }
#define BOOST_AUTO_FUNCTION(signature, return_type, ...) auto signature -> decltype( HANDLE_CONDS(__VA_ARGS__) return_type) BUILD_FUNC_BODY

And now we actually have a working BOOST_AUTO_FUNCTION implementation!

Here's the code the above example would produce: godbolt

template< class L, class R >
auto operator -( L const& lhs, R const& rhs ) -> decltype(
    typename std::enable_if<is_vector_udt< L >::value>::type(),
    typename std::enable_if<is_vector_udt< R >::value>::type(),
    lhs + rhs,
    -rhs,
    std::declval<typename L::value_type>(),
    lhs + -rhs // <- expression from our additional parameter
) {
    return lhs + -rhs;
}

3. Full Code & Boost.PP Implementation

This is the full implementation of our slightly modified BOOST_AUTO_FUNCTION: godbolt

#define CONCAT_IMPL(a, b) a ## b
#define CONCAT(a,b) CONCAT_IMPL(a, b)

#define EXPAND(...) __VA_ARGS__
#define EMPTY()
#define DEFER(id) id EMPTY()

#define HAS_MORE_TOKENS(expr) EXPAND(DEFER(HAS_MORE_TOKENS_RESULT)(HAS_MORE_TOKENS_CHECK expr, 0, 1))
#define HAS_MORE_TOKENS_CHECK(...) ~,~
#define HAS_MORE_TOKENS_RESULT(a, b, c, ...) c

#define IIF(condition, a, b) CONCAT(IIF_, condition)(a, b)
#define IIF_0(a, b) b
#define IIF_1(a, b) a

#define SEQ_HEAD(seq) EXPAND(DEFER(SEQ_HEAD_IMPL)(SEQ_HEAD_EL seq))
#define SEQ_HEAD_EL(el) el,
#define SEQ_HEAD_IMPL(head, tail) head
#define SEQ_TAIL(seq) SEQ_TAIL_IMPL seq
#define SEQ_TAIL_IMPL(el)

#define FOR_EACH(func, seq) IIF(HAS_MORE_TOKENS(seq), FOR_EACH_END, FOR_EACH_0)(func, seq)
#define FOR_EACH_END(...)
#define FOR_EACH_0(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_1)(func, SEQ_TAIL(seq))
#define FOR_EACH_1(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_2)(func, SEQ_TAIL(seq))
#define FOR_EACH_2(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_3)(func, SEQ_TAIL(seq))
#define FOR_EACH_3(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_4)(func, SEQ_TAIL(seq))
#define FOR_EACH_4(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_END)(func, SEQ_TAIL(seq))

#define FOR_EACH_I(func, seq) IIF(HAS_MORE_TOKENS(seq), FOR_EACH_I_END, FOR_EACH_I_0)(func, seq)
#define FOR_EACH_I_END(...)
#define FOR_EACH_I_0(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_1)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_1(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_2)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_2(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_3)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_3(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_4)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_4(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_END)(func, SEQ_TAIL(seq))


#define TUP_SIZE(...) TUP_SIZE_IMPL(,##__VA_ARGS__,3,2,1,0)
#define TUP_SIZE_IMPL(a,b,c,d,e,...) e
#define TUP_TO_SEQ(...) CONCAT(TUP_TO_SEQ_, TUP_SIZE(__VA_ARGS__))(__VA_ARGS__)
#define TUP_TO_SEQ_0()
#define TUP_TO_SEQ_1(a) (a)
#define TUP_TO_SEQ_2(a,b) (a)(b)
#define TUP_TO_SEQ_3(a,b,c) (a)(b)(c)


#define COND_if    HANDLE_IF (
#define COND_try   HANDLE_TRY (

#define COND_IF_typename HANDLE_IF_TYPENAME (

#define HANDLE_IF(expr) IIF(HAS_MORE_TOKENS(expr), HANDLE_IF_MORE, HANDLE_IF_IF)(expr)
#define HANDLE_IF_MORE(expr) CONCAT(COND_IF_,expr))

#define HANDLE_IF_TYPENAME(expr) FOR_EACH(HANDLE_IF_TYPENAME_IMPL, expr)
#define HANDLE_IF_TYPENAME_IMPL(expr) std::declval<typename expr>(),
#define HANDLE_IF_IF(expr) FOR_EACH(HANDLE_IF_IF_IMPL, expr)
#define HANDLE_IF_IF_IMPL(expr) typename std::enable_if<expr::value>::type(),
#define HANDLE_TRY(expr) FOR_EACH(HANDLE_TRY_IMPL, expr)
#define HANDLE_TRY_IMPL(expr) expr,

#define PARSE_COND(expr) CONCAT(COND_,expr))

#define HANDLE_CONDS(...) FOR_EACH_I(PARSE_COND, TUP_TO_SEQ(__VA_ARGS__))
#define BUILD_FUNC_BODY(...) { __VA_ARGS__; }
#define BOOST_AUTO_FUNCTION(signature, return_type, ...) auto signature -> decltype( HANDLE_CONDS(__VA_ARGS__) return_type) BUILD_FUNC_BODY



// Usage:
template< class L, class R >
BOOST_AUTO_FUNCTION( operator -( L const& lhs, R const& rhs ),
                     lhs + -rhs
                   , if ( is_vector_udt< L > )
                        ( is_vector_udt< R > )
                   , try ( lhs + rhs )
                         ( -rhs )
                   , if typename ( L::value_type )
                   )
(
  return lhs + -rhs
)

With Boost Preprocessor we could cut down on a lot of the boilerplate macro code.

This is how the same implementation would look with with boost pp: godbolt

#include <boost/preprocessor.hpp>

#define COND_if    HANDLE_IF (
#define COND_try   HANDLE_TRY (

#define COND_IF_typename HANDLE_IF_TYPENAME (

#define HANDLE_IF(expr) BOOST_PP_IIF(BOOST_PP_IS_BEGIN_PARENS(expr), HANDLE_IF_IF, HANDLE_IF_MORE)(expr)
#define HANDLE_IF_MORE(expr) BOOST_PP_CAT(COND_IF_,expr))

#define HANDLE_IF_TYPENAME(expr) BOOST_PP_SEQ_FOR_EACH(HANDLE_IF_TYPENAME_IMPL, ~, expr)
#define HANDLE_IF_TYPENAME_IMPL(r, _, expr) std::declval<typename expr>(),
#define HANDLE_IF_IF(expr) BOOST_PP_SEQ_FOR_EACH(HANDLE_IF_IF_IMPL, ~, expr)
#define HANDLE_IF_IF_IMPL(r, _, expr) typename std::enable_if<expr::value>::type(),
#define HANDLE_TRY(expr) BOOST_PP_SEQ_FOR_EACH(HANDLE_TRY_IMPL, ~, expr)
#define HANDLE_TRY_IMPL(r, _, expr) expr,

#define PARSE_COND(r, _, i, expr) BOOST_PP_CAT(COND_,expr))

#define TUP_TO_SEQ(...) BOOST_PP_TUPLE_TO_SEQ((__VA_ARGS__))
#define HANDLE_CONDS(...) BOOST_PP_SEQ_FOR_EACH_I( PARSE_COND, ~, TUP_TO_SEQ(__VA_ARGS__))
#define BUILD_FUNC_BODY(...) { __VA_ARGS__; }
#define BOOST_AUTO_FUNCTION(signature, return_type, ...) auto signature -> decltype( HANDLE_CONDS(__VA_ARGS__) return_type) BUILD_FUNC_BODY

4. Additional resources

If I understand correctly you wish to have a function participate in the overload list depending on certain conditions. There are several constructs you can use to achieve your goals. We are going to use std::enable_if_t which leverages SFINAE properties of C++.

You can find the documentation for std::enable_if_t here.

The following solution targets C++11 and above.

First we will create a vector type that has the operators you want and a trait to check that a type matches the type of our vector. Below is a small code example to declare and test this.

#include <iostream>
#include <type_traits>
#include <vector>
#include <cassert>

template<typename V>
struct My_vector
{
    My_vector(std::vector<V> const &init) : v_(init) {}
    My_vector(std::initializer_list<V> const &init) : v_(init) {}

    My_vector operator-()
    {
        std::vector<V> res(v_);
        for (auto & a : res)
            a = -a;
        return res;
    }
    template<typename U>
    My_vector operator+(My_vector<U> const & rhs)
    {
        assert (rhs.v_.size() ==  v_.size());
        std::vector<V> res(v_);
        for(auto l = res.begin(), r = rhs.v_.begin(); l != res.end(); l++, r++)
            *l = *l + *r;
        return res;
    }
    
    std::vector<V> v_;
};
template<typename T>
struct is_my_vector : std::false_type {};

template<typename T>
struct is_my_vector<My_vector<T>> : std::true_type {};

int main(int argc, char const* argv[])
{
    My_vector<int> v1 {1, 2, 3};
    My_vector<double> v2 {4, 5, 6};
    
    auto v3 = v1 + v2;
    auto v4 = -v2;
    
    std::cout << std::boolalpha;
    std::cout << is_my_vector<std::vector<int>>::value << std::endl;
    std::cout << is_my_vector<decltype(v1)>::value << std::endl;
    std::cout << is_my_vector<decltype(v2)>::value << std::endl;
    std::cout << is_my_vector<decltype(v3)>::value << std::endl;
    std::cout << is_my_vector<decltype(v4)>::value << std::endl;

    return 0;   
}

Now we can make declare our - operator like this:

template<
    typename L, 
    typename R, 
    typename = std::enable_if_t<is_my_vector<L>::value, L*>,
    typename = std::enable_if_t<is_my_vector<R>::value, R*>
>
auto operator-(L const & lhs, R const & rhs) -> decltype(lhs + -rhs)
{
    return lhs + -rhs;
}

Declaring it this way with std::enable_if_t will insure that the function will be considered only if L & R are of some My_vector<T>.

Note that the expression in decltype is evaluated at compilation and there is no runtime overhead. I think that from C++14 onwards you can omit the return decltype altogether.

To test this just create a type that has the operator- and operator+ like we did for My_vector and try to substruct one instance from the other = The compilation will fail. If you however remove the typename = std::enable_if_t lines from the template definition you will se your code compiles OK.

This may not be 100% what you want because we are not checking for the existence of the - & + operators in a SFINAE way but since we know our types have these operators it is not necessary

Related