私は実際にこれを行う方法を理解していて、スタックオーバーフローの人はこのメソッドに興味があると思っていました。コードはGitlab repositoryで公開されています。
悲しいことに、完全に一般的な意味では複雑ですが、以下のコードは機能します。 最初に、いくつかのメタプログラミングヘッダーとtype_traitを入れて、 タイプが別のタイプの特殊化であるかどうかを確認します。
#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 {};
私たちは、その後、私たちは 引数の型や関数の引数の数を推定することができますclosure_traits関数を定義します。 これを行う方法について私のquestionに答えた種類の人々には感謝しています。
/* 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;
};
};
は今、私はあなたがアンパックタプル、にあなたを可能にすること、および foreach_in_tuple(STDのジャストバージョンは:: C++ 17から適用されます) に関数を適用することを可能にするヘルパー関数を定義しますタプルの の各要素にファンクタを適用します。これは再帰的に行われるため、ヘルパー関数が必要です。
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))...);
}
}
今、私はその引数を取り、それが既に1でない場合、タプルにラップ関数を定義し、それがある場合はそれだけで同じことを残します。これは素晴らしいことです。なぜなら、別の関数(f)をラップして呼び出すことができ、ラップされた関数がタプルを返すと仮定できるからです。
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));
}
これらの2つの機能は、単にC++ 17 のstdのわずかに少ないジェネリックバージョン::適用し、タプルの各要素にファンクタを適用する機能です。
template<typename FuncType, typename TupleType>
decltype(auto) apply(FuncType&& func, TupleType&& tup) {
return detail::apply_impl(std::forward<FuncType>(func),
std::forward<TupleType>(tup),
std::make_index_sequence<std::tuple_size<
std::decay_t<TupleType>>::value>{});
}
/* Applies a Functor Func to each element of the tuple. As you might
* expect, the Functor needs overloads for all of the types that in
* the tuple or the code will not compile.
*/
template<typename Func, typename TupleType>
decltype(auto) for_each_in_tuple(TupleType&& tup) {
return detail::for_each_impl<Func>(std::forward<TupleType>(tup),
std::make_index_sequence<std::tuple_size<
std::decay_t<TupleType>>::value>{});
}
次の関数は、再帰的方法を使用してアンパックすることにより、引数のそれぞれに関数を適用します。上記のapply_impメソッドとforeach_in_tupl_implメソッドで使用されているのと同じメソッドです。 戻り値もラップします。
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;
}
}
実際にラッピングする機能。
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> {});
}
C++からPython関数を呼び出したいと思っていますか? –
擬似コード(Python)の例が良いでしょう。 – Lunaweaver
@ChrisBritt、いいえ、私はそうは思わない。質問は、「ここにPythonの持つ機能があります.C++にもこの機能がありますか?」 – Kevin