Absence of typeof operator in C++03?

后端 未结 5 1139
半阙折子戏
半阙折子戏 2020-12-05 21:44

I\'m just wondering how boost have implemented BOOST_TYPEOF (in C++03) which seems to be a very useful tool. Anyone has any idea?

Also, I\'m thinking C++03 itself co

5条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-05 22:09

    It just uses compiler magics. Like, GCC's __typeof__. For compilers that don't provide such magic, it provides an emulation that can detect the type of some expressions, but fails with completely unknown types.

    A possible implementation could be to have a list of functions that accept an expression of a given type, and then dispatch from that type to a number using a class template. To make the function template return the number as a compile time entity, we put it into an array dimension

    template struct type2num;
    template struct num2type;
    template typename type2num::dim &dispatch(T const&);
    

    Then it goes from that number back to the type, so that our EMUL_TYPEOF could directly name the type. So to register a type, we write

    #define REGISTER_TYPE(T, N) \
      template<> \
      struct type2num { \
        static int const value = N; \
        typedef char dim[N]; \
      }; \
      template<> \
      struct num2type { typedef T type; }
    

    Having this in place, you can write

    #define EMUL_TYPEOF(E) \
      num2type::type
    

    Whenever you need to register a type, you write

    REGISTER_TYPE(int, 1);
    REGISTER_TYPE(unsigned int, 2);
    // ...
    

    Of course, now you find you need a mechanism to accept vector, where you don't know T in advance and then it gets arbitrary complex. You could create a system where the numbers mean more than just a type. This could probably work:

    #define EMUL_TYPEOF(E) \
      build_type::type
    

    This could detect types like int and also types like shared_ptr - in other words, types that aren't class template specializations, and class template specializations with one template argument, by doing some kind of systematical mapping

    • If the first number yields 1, the second number specifies a type; otherwise
    • the first number specifies a template, and the second number its first type template argument

    So this becomes

    template
    struct build_type {
      typedef typename num2tmp::template apply<
        typename num2type::type>::type type;
    };
    
    template
    struct build_type<1, N> {
      typedef num2type::type type;
    };
    

    We also need to change the dispatch template and split it up in two versions, shown below, alongside the REGISTER_TEMP1 for registering one-argument templates

    template typename type2num::dim1 &dispatch_1(T const&);
    template typename type2num::dim2 &dispatch_2(T const&);
    
    #define REGISTER_TYPE(T, N) \
      template<> \
      struct type2num { \
        static int const value_dim1 = 1; \
        static int const value_dim2 = N; \
        typedef char dim1[value_dim1]; \
        typedef char dim2[value_dim2]; \
      }; \
      template<> \
      struct num2type { typedef T type; }
    
    #define REGISTER_TMP1(Te, N) \
      template \
      struct type2num< Te > { \
        static int const value_dim1 = N; \
        static int const value_dim2 = type2num::value_dim2; \
        typedef char dim1[value_dim1]; \
        typedef char dim2[value_dim2]; \
      }; \
      template<> struct num2tmp { \
        template struct apply { \
          typedef Te type; \
        }; \
      }
    

    Registering the std::vector template and both int variants now look like

    REGISTER_TMP1(std::vector, 2);
    // ... REGISTER_TMP1(std::list, 3);
    
    REGISTER_TYPE(int, 1);
    REGISTER_TYPE(unsigned int, 2);
    // ... REGISTER_TYPE(char, 3);
    

    You probably also want to register multiple numbers with each type, one number for each combination of const/volatile or may need more than one number per type for recording *, & and such. You also want to support vector< vector >, so you need more than one number for the template argument too, making build_type call itself recursively. As you can create an arbitrary long list of integers, you can encode anything into that sequence anyway, so it's just up to your creativity on how to represent these things.

    In the end, you are probably reimplementing BOOST_TYPEOF :)

提交回复
热议问题