從一個例子看現代C++的威力

引子

最近準備重構一下個人kapok庫,讓meta函數能夠返回元素爲kv的tuple,例如:c++

struct person
{
    std::string name;
    int age;
    META(name, age) //定義一個支持變參的meta函數
};
int main()
{
    person p = {「tom」, 20};
    auto tp = p.meta();
    static_assert(std::is_same(std::tuple<std::pair<std::string, int>>, decltype(tp), 「not same」);
    //在內存中是這樣的 {{「name」:」tom」}, {「age」:20}};
    return 0;
}

相似這個META的實現我在msgpack的庫裏看到了,在這裏git

  msgpack中僅僅是宏元的代碼就數百行了,看起來很是複雜,msgpack之因此用這麼複雜的方式去實現恐怕是爲了支持c++98/03標準。原本想看看msgpack是如何實現META函數的,可是它的宏元代碼讀起來比較困難,要讀懂估計要花一天的時間,因而做罷。github

後來想起羣裏的ddrm實現了相似的功能,聽說沒有msgpack這麼複雜,簡潔一些,因而向ddrm要來了代碼(在此對ddrm分享的源碼錶示感謝)。他的思路也是用宏元,可是比msgpack的代碼少不少,將近一百行代碼。我不太喜歡這麼複雜的代碼,我準備用一種更簡單的方式去實現這個效果。附上ddrm的代碼,你們能夠借鑑參考一下。app

#ifndef TUPLE_MACRO_DEF_H
#define TUPLE_MACRO_DEF_H

#define MARCO_EXPAND(arg_list)          arg_list
#define APPLY_VARIADIC_MACRO(macro,tuple) macro tuple

#define ADD_REFERENCE(t) std::reference_wrapper<decltype(t)>(t)
#define ADD_REFERENCE_CONST(t) std::reference_wrapper<std::add_const_t<decltype(t)>>(t)
#define PAIR_OBJECT(t) std::make_pair(#t, ADD_REFERENCE(t))
#define PAIR_OBJECT_CONST(t) std::make_pair(#t, ADD_REFERENCE_CONST(t))
#define MAKE_TUPLE(...) auto tuple() { return std::make_tuple(__VA_ARGS__); } 
#define MAKE_TUPLE_CONST(...) auto tuple() const { return std::make_tuple(__VA_ARGS__); }

/* arg list expand macro, now support 40 args */
#define MAKE_ARG_LIST_1(op, arg, ...)   op(arg)
#define MAKE_ARG_LIST_2(op, arg, ...)   op(arg), MARCO_EXPAND(MAKE_ARG_LIST_1(op, __VA_ARGS__))
#define MAKE_ARG_LIST_3(op, arg, ...)   op(arg), MARCO_EXPAND(MAKE_ARG_LIST_2(op, __VA_ARGS__))
#define MAKE_ARG_LIST_4(op, arg, ...)   op(arg), MARCO_EXPAND(MAKE_ARG_LIST_3(op, __VA_ARGS__))
#define MAKE_ARG_LIST_5(op, arg, ...)   op(arg), MARCO_EXPAND(MAKE_ARG_LIST_4(op, __VA_ARGS__))
#define MAKE_ARG_LIST_6(op, arg, ...)   op(arg), MARCO_EXPAND(MAKE_ARG_LIST_5(op, __VA_ARGS__))
#define MAKE_ARG_LIST_7(op, arg, ...)   op(arg), MARCO_EXPAND(MAKE_ARG_LIST_6(op, __VA_ARGS__))
#define MAKE_ARG_LIST_8(op, arg, ...)   op(arg), MARCO_EXPAND(MAKE_ARG_LIST_7(op, __VA_ARGS__))
#define MAKE_ARG_LIST_9(op, arg, ...)   op(arg), MARCO_EXPAND(MAKE_ARG_LIST_8(op, __VA_ARGS__))
#define MAKE_ARG_LIST_10(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_9(op, __VA_ARGS__))
#define MAKE_ARG_LIST_11(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_10(op, __VA_ARGS__))
#define MAKE_ARG_LIST_12(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_11(op, __VA_ARGS__))
#define MAKE_ARG_LIST_13(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_12(op, __VA_ARGS__))
#define MAKE_ARG_LIST_14(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_13(op, __VA_ARGS__))
#define MAKE_ARG_LIST_15(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_14(op, __VA_ARGS__))
#define MAKE_ARG_LIST_16(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_15(op, __VA_ARGS__))
#define MAKE_ARG_LIST_17(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_16(op, __VA_ARGS__))
#define MAKE_ARG_LIST_18(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_17(op, __VA_ARGS__))
#define MAKE_ARG_LIST_19(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_18(op, __VA_ARGS__))
#define MAKE_ARG_LIST_20(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_19(op, __VA_ARGS__))
#define MAKE_ARG_LIST_21(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_20(op, __VA_ARGS__))
#define MAKE_ARG_LIST_22(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_21(op, __VA_ARGS__))
#define MAKE_ARG_LIST_23(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_22(op, __VA_ARGS__))
#define MAKE_ARG_LIST_24(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_23(op, __VA_ARGS__))
#define MAKE_ARG_LIST_25(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_24(op, __VA_ARGS__))
#define MAKE_ARG_LIST_26(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_25(op, __VA_ARGS__))
#define MAKE_ARG_LIST_27(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_26(op, __VA_ARGS__))
#define MAKE_ARG_LIST_28(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_27(op, __VA_ARGS__))
#define MAKE_ARG_LIST_29(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_28(op, __VA_ARGS__))
#define MAKE_ARG_LIST_30(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_29(op, __VA_ARGS__))
#define MAKE_ARG_LIST_31(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_30(op, __VA_ARGS__))
#define MAKE_ARG_LIST_32(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_31(op, __VA_ARGS__))
#define MAKE_ARG_LIST_33(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_32(op, __VA_ARGS__))
#define MAKE_ARG_LIST_34(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_33(op, __VA_ARGS__))
#define MAKE_ARG_LIST_35(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_34(op, __VA_ARGS__))
#define MAKE_ARG_LIST_36(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_35(op, __VA_ARGS__))
#define MAKE_ARG_LIST_37(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_36(op, __VA_ARGS__))
#define MAKE_ARG_LIST_38(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_37(op, __VA_ARGS__))
#define MAKE_ARG_LIST_39(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_38(op, __VA_ARGS__))
#define MAKE_ARG_LIST_40(op, arg, ...)  op(arg), MARCO_EXPAND(MAKE_ARG_LIST_39(op, __VA_ARGS__))

/* emmbed marco, using EMMBED_TUPLE(5 , a, b, c, d, e) */
//note use MACRO_CONCAT like A##_##B direct may cause marco expand error
#define MACRO_CONCAT(A, B) MACRO_CONCAT1(A, B)
#define MACRO_CONCAT1(A, B) A##_##B

#define MAKE_ARG_LIST(N, op, arg, ...) \
        MACRO_CONCAT(MAKE_ARG_LIST, N)(op, arg, __VA_ARGS__)

#define EMMBED_TUPLE(N, ...) \
MAKE_TUPLE(MAKE_ARG_LIST(N, PAIR_OBJECT, __VA_ARGS__)) \
MAKE_TUPLE_CONST(MAKE_ARG_LIST(N, PAIR_OBJECT_CONST, __VA_ARGS__))

// see  http://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5
#define RSEQ_N() \
         63,62,61,60,                   \
         59,58,57,56,55,54,53,52,51,50, \
         49,48,47,46,45,44,43,42,41,40, \
         39,38,37,36,35,34,33,32,31,30, \
         29,28,27,26,25,24,23,22,21,20, \
         19,18,17,16,15,14,13,12,11,10, \
         9,8,7,6,5,4,3,2,1,0

#define ARG_N( \
         _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
         _61,_62,_63,N, ...) N

#define GET_ARG_COUNT(...) APPLY_VARIADIC_MACRO(GET_ARG_COUNT_INNER,(__VA_ARGS__, RSEQ_N()))
#define GET_ARG_COUNT_INNER(...) APPLY_VARIADIC_MACRO(ARG_N, (__VA_ARGS__))

#define EMMBED(...) EMMBED_TUPLE(GET_ARG_COUNT(__VA_ARGS__), __VA_ARGS__)

#endif

10行代碼解決問題

我探索了一些可能的方案後,最終找到了一種滿意的方案,最終的實現代碼不過十來行,很是簡潔,感謝c++11/14,正是現代C++才讓代碼變得如此簡潔。函數

實現思路也比較簡單,問題的本質是將輸入的參數值變成一個pair,key是值的字面量,value是變量自己的值,因此生成的pair是這樣的std::pair<std::string, someval>, 另外這個pair是每個輸入參數值都會生成一個pair,若是是一系列的參數值就要生成一系列的pair,這就要求支持變參。因此問題的關鍵是要在展開變參的過程當中將這些pair放到一個tuple中,這對於c++11/14來講是不難辦到的,下面是實現代碼:google

#define MAKE_PAIR(text) std::pair<std::string, decltype(text)>{#text, text}

template<typename T>
constexpr static inline auto apply(T const & args)
{
    return args;
}

template<typename T, typename T1, typename... Args>
constexpr static inline auto apply(T const & t, const T1& first, const Args&... args)
{
    return apply(std::tuple_cat(t, std::make_tuple(MAKE_PAIR(first))), args...);
}

#define META(...) auto meta(){ return apply(std::tuple<>(), __VA_ARGS__); }

使用現代C++以後,僅僅須要10行代碼就能夠實現以前須要上百行甚至數百行代碼才能實現的目標,這無疑體現了現代C++的巨大威力。除了很是簡潔的優勢以外,還解決了一個宏元沒法完全解決的問題,宏元須要預先定義一些參數的宏,這些宏定義是有限的,若是參數超出定義的上限就會編譯報錯,而這個變參版本徹底不用擔憂這個問題,支持任意個參數。spa

updatec++11

 

#include <array>
#include <string>
#include <tuple>
template<size_t N>
std::array<std::string, N> split(const std::string& s, const char delimiter)
{
    size_t start = 0;
    size_t end = s.find_first_of(delimiter);

    std::array<std::string, N> output;

    size_t i = 0;
    while (end <= std::string::npos)
    {
        output[i++] = std::move(s.substr(start, end - start));
        if (end == std::string::npos)
            break;

        start = end + 2;
        end = s.find_first_of(delimiter, start);
    }

    return output;
}

template<size_t N, typename T>
static inline auto make(const std::array<std::string, N>&ar, size_t index, const T& args)
{
    return args;
}

template<size_t N, typename T, typename T1, typename... Args>
static inline auto make(const std::array<std::string, N>&ar, size_t index, T const & t, T1& first, Args&... args)
{
    return make(ar, index + 1, std::tuple_cat(t, std::make_tuple(std::pair<std::string, T1&>(ar[index], first))), args...);
}

#define VA_ARGS_NUM(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value

#define META(...) auto meta(){\
    auto ar = split<VA_ARGS_NUM(__VA_ARGS__)>(#__VA_ARGS__, ',');\
    return make(ar, 0, std::tuple<>(), __VA_ARGS__);\
}

 

 

 

後記

這件事給了我一個啓示,若是咱們一直按照前人的路去走,就很難超越前人,若是咱們運用新的技術,改變思路,經常會化繁爲簡,化腐朽爲神奇。只有經過新技術、新思惟去創造、創新才能超越前人。code

相關文章
相關標籤/搜索