STD ::機能を2つ以上のargsで推定します
-
27-10-2019 - |
質問
なぜだろうか std::function
2つのargument関数のみを認識しています。うまく機能しているコードをいくつか書きましたが、多くの制限があります。フィードバック歓迎。特に、私は車輪を再発明していると思います。
私のコードはオンです IDEONE そして、私はそれを参照します。
たとえば、タイプを説明できます main
と:
function_type_deducer(main).describe_me();
// Output: I return i and I take 2 arguments. They are of type: i PPc
(「私」は「int」と「ppc」を意味します。
標準 std::function
2つ以上のARGSを持つ関数では機能しません(コードの最後の2行を参照)が、このコードは行われます(サンプルコードは3-ARG関数を示しています)。たぶん私のデザインは、代わりに標準ライブラリで使用する必要があります!定義します typedef tuple<Args...> args_as_tuple;
最初の2つの引数タイプだけでなく、すべてのargを保存する。
主なトリックは、この機能の控除です。
template<class T, class... Args>
auto function_type_deducer(T(Args...)) -> Function__<T, Args...> {
return Function__<T, Args...> {};
}
制限:
- Lambdasでは機能しません。これはコンパイルされません
function_type_deducer([](){}).describe_me();
- 間に小さな違いがあることに気付かない
x
とy
, 、 なのでy
を取るstring&
, 、 どこx
を取るstring
. 。 (std ::関数もこれに気付かない)
これらのいずれかを修正する方法に関するアイデアはありますか?車輪を再発明しましたか?
解決
これはコンパイルされません
function_type_deducer([](){}).describe_me();
場合は機能します function_type_deducer
テンプレートではありませんでした。 :)非キャプチャラムダス(空 []
)関数ポインターに暗黙的に転換可能です。悲しいことに、いくつかのテンプレート引数控除については、暗黙の変換は考慮されていません。見る この質問 詳細については、コメントが示すように、私の答えは完全に正しくないことに注意してください)。
yは文字列を取るので、xとyの間に小さな違いがあることに気付きません。xは文字列を取得します。
それは関数の問題ではなく、それは問題です typeid
, 、この単純なテストコードが示すように:
template<class T>
void x(void(T)){
T v;
(void)v;
}
void f1(int){}
void f2(int&){}
int main(){
x(f1);
x(f2);
}
IDEONEのライブ例. 。出力:
エラー:「V」は参照として宣言されましたが、初期化されていません
簡単な修正は、タグディスパッチを使用する可能性があります。
#include <type_traits> // is_reference
#include <iostream>
#include <typeinfo>
template<class T>
void print_name(std::true_type){
std::cout << "reference to " << typeid(T).name();
}
template<class T>
void print_name(std::false_type){
std::cout << typeid(T).name();
}
template<class T>
void print_name(){
print_name(typename std::is_reference<T>::type());
}
そして電話してください print_name<NextArg>()
それ以外の typeid(NextArg).name()
.
車輪を再発明しましたか?
はい、ある種のいいえ、あなたはしませんでした。 Boost.Function すべての引数にtypedefsを提供します(argN_type
スタイル)、静的定数と同じくらい arity
その数。ただし、これらのtypedefsに一般的に簡単にアクセスすることはできません。存在しないものに誤ってアクセスしないための回り道が必要です。 tuple
アイデアは最適ですが、より良い方法で書くことができます。これが私がかつて書いたものの変更されたバージョンです:
#include <tuple>
#include <type_traits>
#include <iostream>
#include <typeinfo>
namespace detail{
template<class T>
std::ostream& print_name(std::ostream& os);
template<class T>
std::ostream& print_pointer(std::ostream& os, std::true_type){
typedef typename std::remove_pointer<T>:: type np_type;
os << "pointer to ";
return print_name<np_type>(os);
}
template<class T>
std::ostream& print_pointer(std::ostream& os, std::false_type){
return os << typeid(T).name();
}
template<class T>
std::ostream& print_name(std::ostream& os, std::true_type){
return os << "reference to " << typeid(T).name();
}
template<class T>
std::ostream& print_name(std::ostream& os, std::false_type){
return print_pointer<T>(os, typename std::is_pointer<T>::type());
}
template<class T>
std::ostream& print_name(std::ostream& os){
return print_name<T>(os, typename std::is_reference<T>::type());
}
// to workaround partial function specialization
template<unsigned> struct int2type{};
template<class Tuple, unsigned I>
std::ostream& print_types(std::ostream& os, int2type<I>){
typedef typename std::tuple_element<I,Tuple>::type type;
print_types<Tuple>(os, int2type<I-1>()); // left-folding
os << ", ";
return print_name<type>(os);
}
template<class Tuple>
std::ostream& print_types(std::ostream& os, int2type<0>){
typedef typename std::tuple_element<0,Tuple>::type type;
return print_name<type>(os);
}
} // detail::
template<class R, class... Args>
struct function_info{
typedef R result_type;
typedef std::tuple<Args...> argument_tuple;
static unsigned const arity = sizeof...(Args);
void describe_me(std::ostream& os = std::cout) const{
using namespace detail;
os << "I return '"; print_name<result_type>(os);
os << "' and I take '" << arity << "' arguments. They are: \n\t'";
print_types<argument_tuple>(os, int2type<arity-1>()) << "'\n";
}
};
IDEONEのライブ例. 。出力:
main: I return 'i' and I take '2' arguments. They are:
'i, pointer to pointer to c'
x: I return 'Ss' and I take '3' arguments. They are:
'i, Ss, c'
y: I return 'Ss' and I take '3' arguments. They are:
'i, reference to Ss, c'
他のヒント
Lambda関数を使用した回答へのリンクは、重要なヒントを提供します。関数コールオペレーターのメンバー関数へのポインターを取得する必要があります。つまり、if T
関数オブジェクトです、あなたは見る必要があります &T::operator()
. 。一般化されたsfinaeを使用すると、この関数コールオペレーターが存在するかどうかを判断できます。このようなものを、おそらくややラウンドアバウトの方法でまとめると、これはこれをもたらします(これは、まだサポートされていないLambda関数を除いて、GCCとClangの最近のバージョンでコンパイルされます。
#include <iostream>
#include <sstream>
#include <string>
#include <typeinfo>
#include <functional>
#include <utility>
// -----------------------------------------------------------------------------
struct S {
void f(int, std::string&, void (*)(int)) {}
void g(int, std::string&, void (*)(int)) const {}
};
// -----------------------------------------------------------------------------
template <typename T> struct describer;
template <> struct describer<S>;
template <> struct describer<int>;
template <> struct describer<void>;
template <> struct describer<std::string>;
template <typename T> struct describer<T&>;
template <typename T> struct describer<T*>;
template <typename T> struct describer<T const>;
template <typename T> struct describer<T volatile>;
template <typename T> struct describer<T const volatile>;
template <typename T, int Size> struct describer<T(&)[Size]>;
template <typename T> struct describer {
static std::string type() { return "???"; }
};
template <> struct describer<S> {
static std::string type() { return "S"; }
};
template <> struct describer<void> {
static std::string type() { return "void"; }
};
template <> struct describer<int> {
static std::string type() { return "int"; }
};
template <> struct describer<std::string> {
static std::string type() { return "std::string"; }
};
template <typename T> struct describer<T&> {
static std::string type() { return describer<T>::type() + std::string("&"); }
};
template <typename T> struct describer<T&&> {
static std::string type() { return describer<T>::type() + std::string("&&"); }
};
template <typename T> struct describer<T*> {
static std::string type() { return describer<T>::type() + std::string("&"); }
};
template <typename T> struct describer<T const> {
static std::string type() { return describer<T>::type() + std::string(" const"); }
};
template <typename T> struct describer<T volatile> {
static std::string type() { return describer<T>::type() + std::string(" volatile"); }
};
template <typename T> struct describer<T const volatile> {
static std::string type() { return describer<T>::type() + std::string(" const volatile"); }
};
template <typename T, int Size> struct describer<T(&)[Size]>
{
static std::string type() {
std::ostringstream out;
out << "(array of " << Size << " " << describer<T>::type() << " objects)&";
return out.str();
}
};
template <typename... T> struct description_list;
template <> struct description_list<> { static std::string type() { return std::string(); } };
template <typename T> struct description_list<T> { static std::string type() { return describer<T>::type(); } };
template <typename T, typename... S> struct description_list<T, S...> {
static std::string type() { return describer<T>::type() + ", " + description_list<S...>::type(); }
};
template <typename R, typename... A>
struct describer<R(*)(A...)>
{
static std::string type() {
return "pointer function returning " + describer<R>::type() + " and taking arguments"
+ "(" + description_list<A...>::type() + ")";
}
};
template <typename R, typename S, typename... A>
struct describer<R(S::*)(A...)>
{
static std::string type() {
return "pointer to member function of " + describer<S>::type() + " returning " + describer<R>::type() + " "
"and taking arguments" + "(" + description_list<A...>::type() + ")";
}
};
template <typename R, typename S, typename... A>
struct describer<R(S::*)(A...) const>
{
static std::string type() {
return "pointer to const member function of " + describer<S>::type() + " returning " + describer<R>::type() + " "
"and taking arguments" + "(" + description_list<A...>::type() + ")";
}
};
template <typename T> char (&call_op(decltype(&T::operator())*))[1];
template <typename T> char (&call_op(...))[2];
template <typename T> struct has_function_call_operator { enum { value = sizeof(call_op<T>(0)) == 1 }; };
template <typename T>
typename std::enable_if<!has_function_call_operator<T>::value>::type describe(std::string const& what, T)
{
std::cout << "describe(" << what << ")=" << describer<T>::type() << "\n";
}
template <typename T>
typename std::enable_if<has_function_call_operator<T>::value>::type describe(std::string const& what, T)
{
std::cout << "describe(" << what << ")=function object: " << describer<decltype(&T::operator())>::type() << "\n";
}
int f(std::string, std::string const&, std::string&&) { return 0; }
int g(std::string&, std::string const&) { return 0; }
int main()
{
describe("int", 1);
describe("f", &f);
describe("g", &g);
describe("S::f", &S::f);
describe("S::g", &S::g);
describe("mini-lambda", []{}); // doesn't work with clang, yet.
describe("std::function<int(int(&)[1], int(&)[2], int(&)[4], int(&)[4])>",
std::function<int(int(&)[1], int(&)[2], int(&)[4], int(&)[4])>());
}