2016-03-17 14 views
8

Chcę wydedukować typy parametrów funkcji z ciągu znaków. Podobne do tego, co robi printf.Typ zapytania z ciągu literowego

Obecnie należy wykonać następujące czynności:

#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>{}); 
} 

Problem polega na tym, muszę napisać Ignoruj ​​() na moje wezwanie funkcji ...

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

co chcę to coś w stylu (kontrola tylko w czasie kompilacji):

MyFoo foo("%d Hello World %f %s"); 
foo.Send(10, 20.f, "Hello"); 
+0

Musisz sprawdzić w środowisku wykonawczym, czy typ parametru odpowiada oczekiwanemu typowi zgodnie z zawartością łańcucha – Garf365

+0

Dziękuję, ale chcę mieć kontrolę czasu kompilacji. (Dodano do pytania.) – Viatorus

+1

Musisz sparsować stały ciąg znaków za pomocą meta-programowania C++, aby pobrać argumenty szablonu dla twojej instancji MyFoo, aby zdefiniować parametry elementu Wyślij. Wymagałoby to jednak sprawdzenia bajtu po bajcie, wielu odmian i ograniczeń. Wdrożenie wersji środowiska wykonawczego byłoby lepszym wyborem. – Youka

Odpowiedz

1

mojej najnowszej wersji: Współpracuje z C++ 11 i GCC 4.8 i Clang. Inne nie testowane.

/*! 
* @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

Dziękujemy za pomoc!

Dowolna optymalizacja?

4

można zrobić coś takiego z char_sequence:

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...>>>; 
}; 

przyjmujące do odtworzenia listy typu od dosłownego łańcucha.

A potem

// ... 

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

Powiązane problemy