【问题标题】:How would one write a "meta if else if.." in C++?如何在 C++ 中编写“meta if else if..”?
【发布时间】:2011-05-13 11:35:31
【问题描述】:

我只是在学习 C++ 元编程的基础知识,我想看看其他人如何解决以下问题会很好。此外,很高兴看到使用 Boost 元编程库的解决方案,因为我认为它们是我的黑暗角落。那么问题来了,能不能更优雅地重写一下呢?

假设我们有以下结构:

template <std::size_t size>
struct type_factory
{
  typedef typename type_factory_impl<size>::type type;
};

这个结构应该是typedeftype,取决于size的值。 type_factory_impltype_factory 的实现。用于判断type的算法是:

if(size % bits<unsigned long long>::value == 0)
  typedef unsigned long long type;
else if(size % bits<unsigned long>::value == 0)
  typedef unsigned long type;
else if(size % bits<unsigned int>::value == 0)
  typedef unsigned int type;
else if(size % bits<unsigned short int>::value == 0)
  typedef unsigned short int type;
else if(size % bits<unsigned char>::value == 0)
  typedef unsigned char type;
else
  static_assert(false, "The type should be multiple of 'unsigned char' size");

我已经通过两种方式解决了这个元程序。第一个直接使用模式匹配,第二个使用meta if-else。将以下代码视为两种解决方案之间的通用代码:

#include <cstddef>
#include <climits>

typedef unsigned char      uchar;
typedef unsigned short int usint;
typedef unsigned int       uint;
typedef unsigned long      ulong;
typedef unsigned long long ulonglong;

// Returns how many bits in Unsigned_Type
template <typename Unsigned_Type>
struct bits
{ enum { value = sizeof(Unsigned_Type)*CHAR_BIT }; };

// struct type_factory_impl ...

template <std::size_t size>
struct type_factory
{
  typedef typename type_factory_impl<size>::type type;
};

int main()
{
  auto a = type_factory<8>::type(0);  // unsigned char
  auto b = type_factory<16>::type(0); // unsigned short int
  auto c = type_factory<24>::type(0); // unsigned char
  auto d = type_factory<32>::type(0); // unsigned long
  auto e = type_factory<40>::type(0); // unsigned char
  auto f = type_factory<48>::type(0); // unsigned short int
  auto g = type_factory<56>::type(0); // unsigned char
  auto h = type_factory<64>::type(0); // unsigned long long
}

第一种解决方案:

template <bool is_uchar>
struct unsigned_char
{
  typedef unsigned char type;
  static_assert(is_uchar,
     "error: size must be multiple of 'unsigned char' size"); 
};
template <>
struct unsigned_char <true>
{ typedef uchar type; };

template <bool is_usint, std::size_t size>
struct unsigned_short_int
{ typedef typename
   unsigned_char<size % bits<uchar>::value == 0>::type type; };
template <std::size_t size>
struct unsigned_short_int <true, size>
{ typedef usint type; };

template <bool is_uint, std::size_t size>
struct unsigned_int
{ typedef typename
   unsigned_short_int<size % bits<usint>::value == 0, size>::type type; };
template <std::size_t size>
struct unsigned_int <true, size>
{ typedef uint type; };

template <bool is_ulong, std::size_t size>
struct unsigned_long
{ typedef typename
   unsigned_int<size % bits<uint>::value == 0, size>::type type; };
template <std::size_t size>
struct unsigned_long <true, size>
{ typedef ulong type; };

template <bool is_ulonglong, std::size_t size>
struct unsigned_long_long
{ typedef typename 
   unsigned_long<size % bits<ulong>::value == 0, size>::type type; };
template <std::size_t size>
struct unsigned_long_long <true, size>
{ typedef ulonglong type; };

template <std::size_t size>
struct type_factory_impl
{ typedef typename 
   unsigned_long_long<size % bits<ulonglong>::value == 0, size>::type type; };

第二种解决方案:

template <bool condition, typename Then, typename Else>
struct IF
{ typedef Else type; };
template <typename Then, typename Else>
struct IF <true, Then, Else>
{ typedef Then type; };

template <std::size_t size>
struct type_factory_impl
{
  typedef typename
    IF<size % bits<ulonglong>::value == 0, ulonglong,
      typename IF<size % bits<ulong>::value == 0, ulong,
        typename IF<size % bits<uint>::value == 0, uint,
          typename IF<size % bits<usint>::value == 0, usint,
            typename IF<size % bits<uchar>::value == 0, uchar, uchar>::type
          >::type
        >::type
      >::type
    >::type type;
};

【问题讨论】:

  • @Matthieu M 我不知道 Boost 中有 meta-if :D 谢谢
  • 我想说我不喜欢这两种解决方案...我最初想使用 MPL 提出一个非常聪明的解决方案,但我意识到 MPL 没有我想要的(或者看起来是这样)。我原以为boost::mpl::set 可以很好地让类型按长度排序,但显然不可能插入自己的排序函子:/

标签: c++ boost metaprogramming c++11 if-statement


【解决方案1】:

和你一样,我认为 Boost.MPL 是黑魔法,所以我认为这可能是尝试使用它来回答你的问题的机会。请记住,这是我对这个库的第一次尝试,那里的一些专家可能会提供更好的解决方案。

这个想法是使用boost::mpl::find_if 来查找类型序列中的第一个匹配项。

typedef boost::mpl::vector
    <
        unsigned long long,
        unsigned long,
        unsigned int,
        unsigned short,
        unsigned char
    > type_sequence;

template<std::size_t size>
struct predicate
{
    template<class T>
    struct apply {
        static const bool value = (size % bits<T>::value == 0);
    };
};

template<std::size_t size>
struct type_factory_impl
{
    typedef typename boost::mpl::find_if
        <
            type_sequence,
            typename predicate<size>::apply<boost::mpl::_1>
        >::type iterator_type;

    typedef typename boost::mpl::deref<iterator_type>::type type;
};

这似乎给了我很好的结果:

我没有处理“默认”情况,但我的大脑刚刚开始流鼻血,我稍后会尝试完成我的答案,希望这会有所帮助。

【讨论】:

  • 所有其他类型根据定义都是char 大小的倍数,所以我认为不需要默认情况。
  • @Ben 实际上,type_factory::type 是 boost::mpl::void_ 因为列表中没有一个类型与谓词匹配(模数的结果始终为 2)
  • 现在我明白了。如果 size 实际上不是通过将某些内容乘以 CHAR_BIT 而创建的,那么您可以使用默认大小写。
  • 谢谢,Boost MPL 看起来是一个不错的学习目标。你的解决方案比我的更具可读性:)
【解决方案2】:

专业化有什么问题吗?

template<size_t N>
struct lowest_bit
{
    enum
    {
        lowest_bit_removed = N & (N-1),
        value = N ^ lowest_bit_removed
    };
};

template<size_t size> struct type_factory_impl                  { typedef uchar     type; };
template<> struct type_factory_impl<sizeof(ushort)   *CHAR_BIT> { typedef ushort    type; };
template<> struct type_factory_impl<sizeof(uint)     *CHAR_BIT> { typedef uint      type; };
template<> struct type_factory_impl<sizeof(ulong)    *CHAR_BIT> { typedef ulong     type; };
template<> struct type_factory_impl<sizeof(ulonglong)*CHAR_BIT> { typedef ulonglong type; };

template<size_t size>
struct type_factory
{
    typedef typename type_factory_impl<lowest_bit<size>::value>::type type;
};

【讨论】:

  • shortuintulongulonglong 是否需要不同的大小?否则,一些明确的专业化将会发生冲突。
  • @dionne,这些不是标准化类型。您的实现应该选择一组已知大小不同的类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-22
  • 2023-03-24
  • 1970-01-01
  • 2021-08-27
相关资源
最近更新 更多