funciones - Convirtiendo una función c++ lambda a ac
lambda function c++ 11 (1)
Estoy escribiendo un código de ajuste, donde una biblioteca externa llama a una función c ++ (usando plantillas variadic y tal). El punto crucial es que la biblioteca externa requiere una función c, que normalmente estaría bien, ya que esto es legal:
LibraryFuncType fn = [](params) { ... }
Si bien puedo hacerlo fácilmente a mano, me gustaría automatizar el envoltorio con algo como:
function_(context, "name", myfunc);
Para hacer esto, necesitaría una función similar a:
template <ReturnType, ParamTypes...>
static void function_(Context &ctx, const std::string &name, std::function<ReturnType(ParamTypes...)> fn) {
ctx.registerFunction(name, [fn](State *state) -> int {
Context ctx(state);
return apply_helper<sizeof..(ParamTypes)>::apply(ctx, fn);
});
}
donde el segundo parámetro "ctx.registerFunction" es del tipo LibraryFuncType.
Pero esto, por supuesto, es problemático, porque la conversión lambda ya no es legal debido a la captura de ''fn''. Sin embargo, si no capturo ''fn'', entonces no tendré acceso a él en la lambda.
Creo que la única forma de lidiar con esto es tener una variable estática, pero no me queda claro cuál es la mejor manera de presentarla. La solución actual que tengo es:
template <typename ReturnType, typename... ParamTypes>
struct function_helper {
static std::function<ReturnType(ParamTypes...)> fn;
function_helper(std::function<ReturnType(ParamTypes...)> _fn) {
fn = _fn;
}
static void registerFn(Context &ctx, const std::string &name) {
ctx.registerFn(name, [](state *state) -> int {
Context ctx(state);
return apply_helper<sizeof...<ParamTypes>>::apply(ctx, fn);
});
}
};
template <typename ReturnType, typename... ParamTypes>
std::function<ReturnType(ParamTypes...)> function_helper<ReturnType, ParamTypes...>::fn;
template <typename ReturnType, typename... ParamTypes>
void function_(Context &ctx, const std::string &name, std::function<ReturnType(ParamTypes...)> fn) {
function_helper<ReturnType, ParamTypes...> reg(fn);
reg.registerFn(ctx, name);
}
Aunque técnicamente esto funciona, es claramente peligroso (y hacky), porque si uso ''function_helper'' en dos funciones con la misma firma, establecerá ''fn'' incorrectamente para una de ellas.
Además, podría hacer la misma variable peligrosa-estática simplemente declarando una variable estática en ''function_''. Hice la clase con la esperanza de que condujera a una idea de una forma correcta de evitar el problema.
¿Alguien sabe de una mejor manera de usar una lambda que no requiera una captura (o alternativamente, una forma de convertir una lambda que captura a una función c)?
Una forma de evitar el uso de un valor de puntero de función dentro de su código de registro es convertirlo en un argumento de plantilla. Desafortunadamente, no puedo encontrar una notación realmente buena. Sin embargo, si es aceptable registrar una función usando algo como a continuación, es bastante sencillo de hacer:
RegisterHelper<decltype(foo)>::doRegister<&foo>("foo");
Con esto, RegisterHelper
es una plantilla de clase con una función static
doRegister()
que obtiene el puntero de función como argumento de plantilla. Sería bueno encontrar una forma de llamar directamente a una plantilla de función y hacer que descubra el tipo:
doRegister<&foo>("foo");
Sin embargo, no he encontrado una manera de hacerlo porque las plantillas de funciones no pueden ser parcialmente especializadas (de lo contrario, creo que sería posible). A continuación se muestra un esquema aproximado de cómo podría verse el código. El código no intenta hacer ninguna de las delegaciones que necesitaría hacer para llamar realmente a la función. Simplemente tiene la intención de mostrar cómo se puede pasar un puntero a la función. La demostración codifica algunos tipos, pero solo porque agregar cualquier código de cálculo ocultaría lo que está sucediendo.
#include <string>
#include <iostream>
struct State;
typedef std::string (*function_type)(State*);
void registerFunction(std::string const& name, function_type function)
{
std::cout << "calling ''" << name << "'': " << function(0) << "/n";
}
template <typename T> class RegisterHelper;
template <typename RC, typename... Args>
class RegisterHelper<RC(Args...)>
{
public:
template <RC (*function)(Args...)>
static void doRegister(std::string const& name) {
registerFunction(name, [](State*) -> std::string {
return function(17, 4.0);
});
}
};
std::string foo(int, double) { return "foo"; }
std::string bar(int, double) { return "bar"; }
int main()
{
RegisterHelper<decltype(foo)>::doRegister<&foo>("foo");
RegisterHelper<decltype(bar)>::doRegister<&bar>("bar");
}