2016-03-17 7 views
8

文字列から関数のパラメータ型を推測したい。 printfと同様です。問題は、私は私の関数呼び出しに()無視記述する必要があるリテラル文字列から型を除外する

#include <utility> 

// calculate the length of a literal string 
constexpr int length(const char* str) 
{ 
    return *str ? 1 + length(str + 1) : 0; 
} 

struct Ignore { 
}; 

template <char C1, char C2> 
struct Type { 
    typedef Ignore type; 
}; 

// %d -> int 
template <> 
struct Type<'%','d'> { 
    typedef int type; 
}; 

// %f -> float 
template <> 
struct Type<'%','f'> { 
    typedef float type; 
}; 

// Get type from string 
template <const char * const * const STR, int POS, int N = length(STR[POS])> 
struct GetType { 
    typedef Ignore type; 
}; 

template <const char * const * const STR, int POS> 
struct GetType<STR, POS, 2> { 
    typedef typename Type<STR[POS][0],STR[POS][1]>::type type; 
}; 

// My dummy class 
template <typename... Targs> 
struct Foo 
{ 
    void Send(Targs...) const {} 
}; 

// Deduce type for each literal string array 
template <const char * const * STRS, std::size_t N, std::size_t... index> 
constexpr auto parseIt(std::index_sequence<index...>) { 
    return Foo<typename GetType<STRS, index>::type...>(); 
} 

template <const char * const * STRS, std::size_t N> 
constexpr auto makeFoo(const char * const (&a)[N]) { 

    return parseIt<STRS, 2>(std::make_index_sequence<N>{}); 
} 

...

constexpr const char *message[] = {"%d", " hello ", "%f", "good"}; 
constexpr auto foo = makeFoo<message>(message); 

int main() 
{ 
    foo .Send(10, Ignore(), 20.0f, Ignore()); 

    return 0; 
} 

Live Example

私が欲しいもの:

は現在、私は次の操作を行います次のようなものです(コンパイル時のチェックのみ):

MyFoo foo("%d Hello World %f %s"); 
foo.Send(10, 20.f, "Hello"); 
文字列リテラルからタイプのリストを取得することができ

template <char ... > struct char_sequence {}; 

template <typename ... Tuples> 
using tuple_concat = decltype(std::tuple_cat(std::declval<Tuples>()...)); 

template <typename> struct format_helper; 

template <typename T> 
using format_helper_t = typename format_helper<T>::type; 

// end case 
template <> 
struct format_helper<char_sequence<>> 
{ 
    using type = std::tuple<>; 
}; 

// general case 
template <char C, char...Cs> 
struct format_helper<char_sequence<C, Cs...>> 
{ 
    using type = format_helper_t<char_sequence<Cs...>>; 
}; 

template <typename T> 
struct dependant_false : std::false_type {}; 

// unknown format % 
template <char...Cs> 
struct format_helper<char_sequence<'%', Cs...>> 
{ 
    static_assert(dependant_false<char_sequence<Cs...>>::value, "Unsupported escape"); 
}; 

// %% for % 
template <char...Cs> 
struct format_helper<char_sequence<'%', '%', Cs...>> 
{ 
    using type = format_helper_t<char_sequence<Cs...>>; 
}; 

// %f float 
template <char...Cs> 
struct format_helper<char_sequence<'%', 'f', Cs...>> 
{ 
    using type = tuple_concat<std::tuple<float>, format_helper_t<char_sequence<Cs...>>>; 
}; 

// %d int 
template <char...Cs> 
struct format_helper<char_sequence<'%', 'd', Cs...>> 
{ 
    using type = tuple_concat<std::tuple<int>, format_helper_t<char_sequence<Cs...>>>; 
}; 

+0

パラメータタイプが文字列の内容に応じて予想されるタイプに対応するかどうかを実行時に確認する必要があります – Garf365

+0

ありがとうございますが、コンパイル時のチェックが必要です。 (質問に追加されました) – Viatorus

+1

MyFooインスタンスのテンプレート引数を取得してメンバSendのパラメータを定義するには、C++メタプログラミングを使用して静的定数文字列を解析する必要があります。しかし、それはバイト単位で、多くのバリエーションと限界をチェックする必要があります。ランタイムバージョンを実装するほうがよいでしょう。 – Youka

答えて

1

私の最新バージョン:C++ 11とGCC 4.8とクランで動作します。テストされていないもの

/*! 
* @brief Variadic template contains all parsed types. 
*/ 
template <typename ...T> 
struct TypeHolder 
{ 
}; 

/*! 
* @brief Identifier for non formating sequence. 
*/ 
struct Unknown {}; 

/*! 
* @brief Identifier for possible formating sequence. 
*/ 
struct Formater {}; 

/*! 
* @brief Any character. 
*/ 
template <char C, typename TYPE, const char *STR, int POS, typename...T> 
struct Format 
{ 
    using type = typename Format<STR[POS], Unknown, STR, POS + 1, T...>::type; 
}; 

/*! 
* @brief Null-terminator. 
*/ 
template <const char *STR, typename TYPE, int POS, typename...T> 
struct Format<'\0', TYPE, STR, POS, T...> { 

    using type = TypeHolder<T...>; 
}; 

/*! 
* @brief Indicates a formation. 
*/ 
template <const char *STR, int POS, typename...T> 
struct Format<'%', Unknown, STR, POS, T...> { 

    using type = typename Format<STR[POS], Formater, STR, POS + 1, T...>::type; 
}; 

/*! 
* @brief Formation was a escape sequence for %. 
*/ 
template <const char *STR, int POS, typename...T> 
struct Format<'%', Formater, STR, POS, T...> { 
    using type = typename Format<STR[POS], Unknown, STR, POS + 1, T...>::type; 
}; 

/*! 
* @brief Formation of an integer. 
*/ 
template <const char *STR, int POS, typename...T> 
struct Format<'d', Formater, STR, POS, T...> { 
    // Append int to variadic template. 
    using type = typename Format<STR[POS], Unknown, STR, POS + 1, T..., int>::type; 
}; 

/*! 
* @brief Formation of an unsigned integer. 
*/ 
template <const char *STR, int POS, typename...T> 
struct Format<'u', Formater, STR, POS, T...> { 
    // Append unsigned int to variadic template. 
    using type = typename Format<STR[POS], Unknown, STR, POS + 1, T..., unsigned int>::type; 
}; 

/*! 
* @brief Formation of a float. 
*/ 
template <const char *STR, int POS, typename...T> 
struct Format<'f', Formater, STR, POS, T...> { 
    // Append float to variadic template. 
    using type = typename Format<STR[POS], Unknown, STR, POS + 1, T..., float>::type; 
}; 

/*! 
* @brief Unknown formatting. 
*/ 
template <char C, const char *STR, int POS, typename...T> 
struct Format<C, Formater, STR, POS, T...> { 
    // Compile time error for unknown formatting. 
    static_assert(sizeof...(T) != sizeof...(T), "Unknown formattion."); 
    using type = TypeHolder<>; 
}; 

/*! 
* @brief 
*/ 
template <const char *STR> 
struct GetTypeFromString { 
    using type = typename Format<STR[0], Unknown, STR, 1>::type; 
}; 

template<typename> 
class Foo; 

template<typename... Ts> 
struct Foo<TypeHolder<Ts...>> 
{ 
    void call(Ts...) const { 

    } 
}; 

constexpr const char message[] = " %d %u %% Hello World %d %f"; 
constexpr auto foo = Foo<GetTypeFromString<message>::type>(); 

int main() { 

    foo.call(int(), int(), int(), float()); 

    return 0; 
} 

Live Example

あなたの助けをありがとう!

最適化はありますか?

4

あなたはchar_sequenceでそのような何かを行うことができます。

そして

// ... 

template <typename... Ts> 
struct Foo 
{ 
    // ... 
    void Send(Ts... args) const; 
}; 

template <typename T> struct tag{}; 

template <typename... Ts> 
Foo<Ts...> MakeFoo(tag<std::tuple<Ts...>>, const std::string& s) 
{ 
    return Foo<Ts...>(s); 
} 

template <char ... Cs> 
auto MakeFoo(char_sequence<Cs...>) 
{ 
    const char s[] = {Cs..., '\0'}; 
    return MakeFoo(tag<format_helper_t<char_sequence<Cs...>>>{}, s); 
} 

Demo

関連する問題