c++ - minecraft splash
Código duplicado usando c++ 11 (6)
Actualmente estoy trabajando en un proyecto y tengo el siguiente problema.
Tengo un método C ++ que quiero trabajar de dos maneras diferentes:
void MyFunction()
{
foo();
bar();
foobar();
}
void MyFunctionWithABonus()
{
foo();
bar();
doBonusStuff();
foobar();
}
Y me gustaría no duplicar mi código porque la función real es mucho más larga. El problema es que bajo ninguna circunstancia debo agregar tiempo de ejecución al programa cuando se llama a MyFunction en lugar de MyFunctionWithABonus. Es por eso que no puedo simplemente tener un parámetro booleano que verifico con una comparación de C ++.
Mi idea hubiera sido usar plantillas de C ++ para duplicar virtualmente mi código, pero no puedo pensar en una forma de hacerlo en la que no tenga tiempo de ejecución adicional y no tenga que duplicar el código.
No soy un experto en plantillas, por lo que me puede faltar algo.
¿Alguno de ustedes tiene una idea? ¿O es simplemente imposible en C ++ 11?
Algo así hará bien:
template<bool bonus = false>
void MyFunction()
{
foo();
bar();
if (bonus) { doBonusStuff(); }
foobar();
}
Llámalo a través de:
MyFunction<true>();
MyFunction<false>();
MyFunction(); // Call myFunction with the false template by default
La plantilla "fea" se puede evitar agregando algunos envoltorios agradables a las funciones:
void MyFunctionAlone() { MyFunction<false>(); }
void MyFunctionBonus() { MyFunction<true>(); }
Puede encontrar información agradable sobre esa técnica there . Ese es un documento "viejo", pero la técnica en sí misma sigue siendo totalmente correcta.
Siempre que tenga acceso a un buen compilador de C ++ 17, incluso puede impulsar aún más la técnica, utilizando constexpr si , de esta manera:
template <int bonus>
auto MyFunction() {
foo();
bar();
if constexpr (bonus == 0) { doBonusStuff1(); }
else if constexpr (bonus == 1) { doBonusStuff2(); }
else if constexpr (bonus == 2) { doBonusStuff3(); }
else if constexpr (bonus == 3) { doBonusStuff4(); }
// Guarantee that this function will not compile
// if a bonus different than 0,1,2,3 is passer
else { static_assert(false);},
foorbar();
}
Aquí hay una ligera variación en la respuesta de Jarod42 usando plantillas variadas para que la persona que llama pueda proporcionar cero o una función de bonificación:
void callBonus() {}
template<typename F>
void callBonus(F&& f) { f(); }
template <typename ...F>
void MyFunction(F&&... f)
{
foo();
bar();
callBonus(std::forward<F>(f)...);
foobar();
}
Código de llamada:
MyFunction();
MyFunction(&doBonusStuff);
Con template y lambda, puedes hacer:
template <typename F>
void common(F f)
{
foo();
bar();
f();
foobar();
}
void MyFunction()
{
common([](){});
}
void MyFunctionWithABonus()
{
common(&doBonusStuff);
}
o bien, simplemente puede crear la función de
prefix
y
suffix
.
void prefix()
{
foo();
bar();
}
void suffix()
{
foobar();
}
void MyFunction()
{
prefix();
suffix();
}
void MyFunctionWithABonus()
{
prefix();
doBonusStuff();
suffix();
}
Otra versión, usando solo plantillas y sin funciones de redireccionamiento, ya que dijiste que no querías ninguna sobrecarga de tiempo de ejecución. Por lo que a mí respecta, esto solo aumenta el tiempo de compilación:
#include <iostream>
using namespace std;
void foo() { cout << "foo/n"; };
void bar() { cout << "bar/n"; };
void bak() { cout << "bak/n"; };
template <bool = false>
void bonus() {};
template <>
void bonus<true>()
{
cout << "Doing bonus/n";
};
template <bool withBonus = false>
void MyFunc()
{
foo();
bar();
bonus<withBonus>();
bak();
}
int main(int argc, const char* argv[])
{
MyFunc();
cout << "/n";
MyFunc<true>();
}
output:
foo
bar
bak
foo
bar
Doing bonus
bak
Ahora solo hay una versión de
MyFunc()
con el parámetro
bool
como argumento de plantilla.
Puede usar el despacho de etiquetas y la sobrecarga de funciones simples:
struct Tag_EnableBonus {};
struct Tag_DisableBonus {};
void doBonusStuff(Tag_DisableBonus) {}
void doBonusStuff(Tag_EnableBonus)
{
//Do bonus stuff here
}
template<class Tag> MyFunction(Tag bonus_tag)
{
foo();
bar();
doBonusStuff(bonus_tag);
foobar();
}
Esto es fácil de leer / comprender, se puede ampliar sin problemas (y sin repeticiones
if
cláusulas, al agregar más etiquetas), y por supuesto no dejará huella en tiempo de ejecución.
La sintaxis de las llamadas es bastante amigable, pero por supuesto puede incluirse en llamadas de vainilla:
void MyFunctionAlone() { MyFunction(Tag_DisableBonus{}); }
void MyFunctionBonus() { MyFunction(Tag_EnableBonus{}); }
El despacho de etiquetas es una técnica de programación genérica ampliamente utilizada, here hay una buena publicación sobre los conceptos básicos.
Teniendo en cuenta algunos de los comentarios que ha realizado el OP con respecto a la depuración, aquí hay una versión que llama a
doBonusStuff()
para compilaciones de depuración, pero no versiones de lanzamiento (que definen
NDEBUG
):
#if defined(NDEBUG)
#define DEBUG(x)
#else
#define DEBUG(x) x
#endif
void MyFunctionWithABonus()
{
foo();
bar();
DEBUG(doBonusStuff());
foobar();
}
También puede usar la
macro de
assert
si desea verificar una condición y fallar si es falsa (pero solo para compilaciones de depuración; las compilaciones de lanzamiento no realizarán la verificación).
Tenga cuidado si
doBonusStuff()
tiene efectos secundarios, ya que estos efectos secundarios no estarán presentes en las versiones de lanzamiento y pueden invalidar las suposiciones hechas en el código.