Ich habe tatsächlich herausgefunden, wie dies zu tun ist, und dachte, dass Leute auf Stapelüberlauf an der Methode interessiert sein könnten. Der Code ist öffentlich unter Gitlab repository verfügbar.
Leider ist es ziemlich kompliziert in voller Allgemeinheit, aber der Code unten funktioniert. Fügen Sie zuerst einige Metaprogrammierungsheader und einen Type_trait hinzu, um zu prüfen, ob ein Typ eine Spezialisierung eines anderen Typs ist.
#include <type_traits>
#include <tuple>
#include <utility>
#include <functional>
#include "arma_wrapper.h"
/* Tests if U is a specialization of T */
template<template<typename...> typename T, typename U>
struct is_specialization_of : std::false_type {};
template<template <typename ...> typename T, typename... Args>
struct is_specialization_of<T, T<Args...>> : std::true_type {};
Wir definieren dann eine closure_traits Funktion, die uns die Argumenttypen und die Anzahl der Argumente einer Funktion ableiten lässt. Ich bin dankbar für die freundlichen Leute, die meine question in Bezug darauf, wie dies zu tun ist beantwortet.
/* For generic types use the type signature of their operator() */
template <typename T>
struct closure_traits :
public closure_traits<decltype(&T::operator())> {};
/*
* This is adapted from the stack overflow question
* Is it possible to figure out the parameter type and return type
* of a lambda.
*/
template <typename ClassType, typename ReturnType,
typename... ArgTypes>
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args)
const>
{
using arity = std::integral_constant<std::size_t,
sizeof...(ArgTypes)>;
using Ret = ReturnType;
template <std::size_t I>
struct Args {
using type = typename std::tuple_element<I,
std::tuple<ArgTypes...>>::type;
};
};
template <typename ClassType, typename ReturnType,
typename... ArgTypes>
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args)>
{
using arity = std::integral_constant<std::size_t,
sizeof...(ArgTypes)>;
using Ret = ReturnType;
template <std::size_t I>
struct Args {
using type = typename std::tuple_element<I,
std::tuple<ArgTypes...>>::type;
};
};
Jetzt definiere ich auf Hilfsfunktionen, mit denen Sie eine Funktion ein entpackten Tupel, anzuwenden (Nur eine Version von std :: gelten von C++ 17) und foreach_in_tuple, die Ihnen erlaubt, Wenden Sie einen Funktor auf jedes Element im Tupel an. Dies ist rekursiv, weshalb Sie die Hilfsfunktionen benötigen.
namespace detail {
/*
* This function defines a generic lambda that takes can be applied
* to every one of the arguments in the tuple. It requires that
* Func has a overload for operator() that can be applied to each of
* the parameters. Then it applies this lambda to each of the
* parameters in the tuple.
*/
template<typename Func, typename TupleType, std::size_t... I>
decltype(auto) for_each_impl(TupleType&& tup,
std::index_sequence<I...>) {
auto func_impl = [] (auto&& x) {
Func func;
return func(std::forward<decltype(x)>(x));
};
return std::make_tuple(func_impl(std::get<I>
(std::forward<TupleType>(tup)))...);
}
/* My version of c++17 apply_impl method. */
template<typename FuncType, typename TupleType, std::size_t... I>
decltype(auto) apply_impl(FuncType&& func, TupleType&& tup,
std::index_sequence<I...>) {
return func(std::get<I>(std::forward<TupleType>(tup))...);
}
}
Nun definiere ich eine Funktion, die ihr Argument nimmt und wickelt es in einem Tupel, wenn es nicht bereits ist, aber wenn es sie ist es nur Blätter die gleiche. Das ist nett, weil Sie dann eine andere Funktion (f) mit ihm umbrechen und dann annehmen können, dass die umbrochene Funktion ein Tupel zurückgibt.
template<typename T>
auto idempotent_make_tuple(T&& arg) ->
std::enable_if_t<is_specialization_of<std::tuple,T>::value, T&&> {
return arg;
}
template<typename T>
auto idempotent_make_tuple(T&& arg) ->
std::enable_if_t<! is_specialization_of<std::tuple, T>::value,
decltype(std::make_tuple(std::forward<T>(arg)))> {
return std::make_tuple(std::forward<T>(arg));
}
Diese beiden Funktionen sind nur etwas weniger generische Version von C++ 17 std :: anwenden und eine Funktion, die einen Funktor auf jedes Element in einem Tupel gilt.
Die folgende Funktion wendet die Funktion auf jedes Argument an, indem sie es mit rekursiven Methoden entpackt. Es ist die gleiche Methode, die in den Methoden apply_imp und foreach_in_tupl_impl oben verwendet wird. Es werden auch die Rückgabewerte umbrochen.
namespace detail {
/*
* This function takes a function and an index sequence with its
* number of arguments. It then figures out the types of its
* arguments, and creates a new function with each of the
* arguments and each of the returned values converted to the
* new types.
*/
template<typename ArgWrapper, typename ReturnWrapper,
typename FuncType, size_t...I>
auto wrap_impl(FuncType&& func, std::index_sequence<I...>) {
/*
* This is used to figure out what the argument types of
* func are
*/
using traits = closure_traits<
typename std::decay_t<FuncType>>;
auto wrapped_func = [=] (std::result_of_t<ReturnWrapper(
typename traits:: template Args<I>::type)>... args) {
/*
* Apply the argument wrapper function to each of the
* arguments of the new function.
*/
decltype(auto) tup1 = for_each_in_tuple<ArgWrapper>
(std::forward_as_tuple(args...));
/* Apply the old function to the wrapped arguments. */
decltype(auto) tup2 = idempotent_make_tuple(apply(func,
std::forward<std::decay_t<decltype(tup1)>>(tup1)));
/*
* Apply the Return wrapper to the return value of the
* old function
*/
decltype(auto) tup3 = for_each_in_tuple<ReturnWrapper>(
std::forward<std::decay_t<decltype(tup2)>>(tup2));
return tup3;
};
return wrapped_func;
}
}
Die Funktion, die tatsächlich die Verpackung ausführt.
template<typename ArgWrapper, typename ReturnWrapper,
typename FuncType>
auto wrap(FuncType&& func) {
return detail::wrap_impl<ArgWrapper, ReturnWrapper>(
std::forward<FuncType>(func),
std::make_index_sequence<closure_traits<
FuncType>::arity::value> {});
}
Sie möchten also eine Python-Funktion aus C++ aufrufen? –
Pseudo-Code (dh. Python) Beispiel wäre nett. – Lunaweaver
@ChrisBritt, nein, das glaube ich nicht. Die Frage ist eher wie "Hier ist ein Feature, das Python hat. Hat C++ diese Funktion auch?" – Kevin