E_G
E_G

Reputation: 337

boost tuple partial iteration?

I'm trying to use boost tuples to avoid some virtual function overhead, and I can't quite make it work. I have a vector of "handlers" that try to handle an input, and once one of them returns true, I don't want to call the rest of them.

C++11 is not available.

First, the current virtual implementation looks like this:

std::vector<Handler*> handlers;

//initialization code...
handlers.push_back(new Handler1);
handlers.push_back(new Handler2);
handlers.push_back(new Handler3);

//runtime code
for(std::vector<Handler*>::iterator iter = handlers.begin();
    iter != handlers.end() && !(*iter)->handle(x); ++iter) {}

Since I have all the types at compiletime, I'd prefer to be able to express this as a tuple, like this:

boost::tuple<Handler1,Handler2,Handler3> handlers;

//runtime code
???
// should compile to something equivalent to:
// if(!handlers.get<0>().handle(x))
//   if(!handlers.get<1>().handle(x))
//     handlers.get<2>().handle(x);

Ideally there would be no virtual function calls and any empty function bodies would inline out. It seems like this might almost be possible with boost::fusion for_each, but I need the short circuiting behavior where once one of the handlers returns true, the rest of them are not called.

Upvotes: 1

Views: 193

Answers (2)

Potatoswatter
Potatoswatter

Reputation: 137780

Simple template recursion should generate a suitable chain of tail-call functions.

template< typename head, typename ... rest >
void apply_handler_step( thing const &arg, std::tuple< head, rest ... > * ) {
    if ( ! handler< head >( arg ) ) {
        return apply_handler_step( arg, (std::tuple< rest ... >*)nullptr );
    } // if handler returns true, then short circuit.
}

void apply_handler_step( thing const &arg, std::tuple<> * ) {
    throw no_handler_error();
}

If you want to put function pointers to handlers in the tuple, then you'll want to recurse with an index value and use get< n >( handler_tuple ), but the principle is the same.

Upvotes: 1

Pubby
Pubby

Reputation: 53027

You could try something like this:

void my_eval() {}
template<typename T, typename... Ts>
void my_eval(T& t, Ts&... ts) {
  if(!t.handle(/* x */))
    my_eval(ts...); 
}

template<int... Indices>
struct indices {
  using next_increment = indices<Indices..., sizeof...(Indices)>;
};

template<int N>
struct build_indices {
  using type = typename build_indices<N - 1>::type::next_increment;
};
template<>
struct build_indices<0> {
  using type = indices<>;
};

template<typename Tuple, int... Indices>
void my_call(Tuple& tuple, indices<Indices...>) {
    my_eval(std::get<Indices>(tuple)...);
}
template<typename Tuple>
void my_call(Tuple& tuple) {
    my_call(tuple, typename build_indices<std::tuple_size<Tuple>::value>::type());
}

To use just pass your tuple to my_call.

Upvotes: 1

Related Questions