1

J'essaie d'utiliser les tuples boost pour éviter certains frais généraux de la fonction virtuelle, et je ne peux pas tout à fait le faire fonctionner. J'ai un vecteur de "handlers" qui essaye de gérer une entrée, et une fois que l'un d'entre eux retourne vrai, je ne veux pas appeler le reste d'entre eux.boost tuple itération partielle?

En premier lieu, la mise en œuvre virtuelle actuelle ressemble à ceci:

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) {} 

Depuis que j'ai tous les types à compiletime, je préférerais être en mesure d'exprimer cela comme un tuple, comme ceci:

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); 

Idéalement, il n'y aurait pas d'appels de fonction virtuelle et tout corps de fonction vide serait aligné. Il semble que ce soit presque possible avec boost::fusion for_each, mais j'ai besoin du comportement de court-circuit où une fois que l'un des gestionnaires retourne vrai, le reste d'entre eux ne sont pas appelés.

Répondre

1

Vous pouvez essayer quelque chose comme ceci:

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()); 
} 

Pour utiliser il suffit de passer votre tuple à my_call.

+0

Ça a l'air bien. Si je vais essayer de le faire sans C++ 11, je devrais juste lister manuellement les cas pour my_eval et les index? –

+1

@E_G Vous pouvez les générer avec Boost.PP je présume. Pas vraiment amusant cependant. – Pubby

+0

Sans C++ 11, créez simplement un modèle sur le nombre de gestionnaires à appeler total et le nombre restant. 'get' la différence. Recurse et se spécialise sur 0 gestionnaires laissés à l'échec et non recurse. – Yakk

1

Une récursivité de modèle simple devrait générer une chaîne appropriée de fonctions d'appel arrière.

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(); 
} 

Si vous voulez mettre des pointeurs de fonction pour les gestionnaires dans le tuple, alors vous aurez envie de récursivité avec une valeur d'index et utiliser get<n>(handler_tuple), mais le principe est le même.

Questions connexes