优化编译时常量

时间:2012-03-02 13:48:55

标签: c++ templates metaprogramming boost-mpl

我正在尝试实现类似std :: pair的类,但是有两个以上的组件。因为在我的应用程序中可能会发生某些元组件在编译时已知,我希望进行以下空间优化:当我知道组件是编译时常量时,只需将其声明为' static const'member,这样它就不会浪费个别类实例中的存储空间。 const限定符确保在运行时修改值的任何尝试都将导致编译错误,至少如果我们排除了不礼貌的const_cast(s)。

我最终得到了以下实现,一个ntuple类

template<typename T0_ = void,
     typename T1_ = void,
     typename T2_ = void,
     typename T3_ = void 
     > class ntuple;

和一个用于标记编译时常量的类

template<class type_, type_ value_> class constant 
{
   typedef type_ type;
   static const type value = value_;
};

和ntuple类的一些部分特化

template<typename T0_>
class ntuple<
 T0_
> {
public:
static const int n=1;
typedef T0_ T0;
static const bool is_static = false;
static const bool is_static0 = false;
T0_ i0;
};

template<
typename T0_, T0_ value0_
>
class ntuple<
constant<T0_, value0_>
> {
public:
static const int n=1;
typedef T0_ T0;
static const bool is_static = true;
static const bool is_static0 = true;
static const T0_ i0 = value0_;
};

template<
typename T0_, T0_ value0_
> const T0_ ntuple<
constant<T0_, value0_> >::i0;

template<
typename T0_,
typename T1_
>
class ntuple<
T0_,
T1_
> {
public:
static const int n=2;
typedef T0_ T0;
typedef T1_ T1;
static const bool is_static = false;
static const bool is_static0 = false;
static const bool is_static1 = false;
T0_ i0;
T1_ i1;
};

template<
typename T0_,
typename T1_, T1_ value1_
>
class ntuple<
T0_,
constant<T1_, value1_>
> {
public:
static const int n=2;
typedef T0_ T0;
typedef T1_ T1;
static const bool is_static = false;
static const bool is_static0 = false;
static const bool is_static1 = true;
T0_ i0;
static const T1_ i1 = value1_;
};

template<
typename T0_,
typename T1_, T1_ value1_
> const T1_ ntuple<
T0_,
constant<T1_, value1_> >::i1;

template<
typename T0_, T0_ value0_,
typename T1_
>
class ntuple<
constant<T0_, value0_>,
T1_
> {
public:
static const int n=2;
typedef T0_ T0;
typedef T1_ T1;
static const bool is_static = false;
static const bool is_static0 = true;
static const bool is_static1 = false;
static const T0_ i0 = value0_;
T1_ i1;
};

template<
typename T0_, T0_ value0_,
typename T1_
> const T0_ ntuple<
constant<T0_, value0_>,
T1_ >::i0;

template<
typename T0_, T0_ value0_,
typename T1_, T1_ value1_
>
class ntuple<
constant<T0_, value0_>,
constant<T1_, value1_>
> {
public:
static const int n=2;
typedef T0_ T0;
typedef T1_ T1;
static const bool is_static = true;
static const bool is_static0 = true;
static const bool is_static1 = true;
static const T0_ i0 = value0_;
static const T1_ i1 = value1_;
};

template<
typename T0_, T0_ value0_,
typename T1_, T1_ value1_
> const T0_ ntuple<
constant<T0_, value0_>,
constant<T1_, value1_> >::i0;

template<
typename T0_, T0_ value0_,
typename T1_, T1_ value1_
> const T1_ ntuple<
constant<T0_, value0_>,
constant<T1_, value1_> >::i1;

这样,成员标记为常量&lt;。,。&gt;不存储为类成员,从而减少对象大小。所需的部分特化的数量可能很大,N = 1,2,3,4的2 ^ N我只报告N = 2:我写了一个简单的脚本来生成所有这些。该类可以如下使用

ntuple<int, int, bool> tup1;
tup1.i0=2;
tup1.i1=0;
tup1.i2=true;
assert (tup1.i0==2);
assert (tup1.i1==0);
assert (tup1.i2==true);

ntuple<int, constant<int, 3>, constant<bool, false> > tup2;
tup2.i0=2;
// tup2.i1=0;  // cannot be assigned, is static a constant
// tup2.i2=true; // cannot be assigned, is static a constant 
assert (tup2.i0==2);
assert (tup2.i1==3);
assert (tup2.i2==false);

assert (sizeof(tup1)>sizeof(tup2));

像这样,这个课程完美无缺。现在,我只想改进宣言 ntuples的语法如下

ntuple<int, int_<3>, bool_<true> >

而不是

ntuple<int, constant<int, 3>, constant<bool, true> >

其中int_和bool_可以定义为

template<int i> struct int_ : constant<int, i> {};
template<bool b> struct bool_ : constant<bool, b> {};

或者我可以使用boost :: mpl类似物,这不是重点。为了实现这一点,简单的解决方案是编写另一个脚本并为常量和非常量模板参数的所有排列生成所有可能的特化,其中常量模板参数可以是int_,bool_,char_等。这是可行的,但代价是部分专业化数量的因子增加我想改变ntuple类的定义如下

 template<typename T0_ = void,
 typename T1_ = void,
 typename T2_ = void,
 typename T3_ = void,
 bool const0 = is_const<T0_>::value,
 bool const1 = is_const<T1_>::value,
 bool const2 = is_const<T2_>::value,
 bool const3 = is_const<T3_>::value
 > class ntuple;

template <class T> is_const { static const bool value = false; };
template <int i> is_const<int_<i> > { static const bool value = true; };
template <bool b> is_const<bool_<b> > { static const bool value = true; };

并专门化ntuple如下

 template<typename T0_,
 typename T1_,
 typename T2_,
 typename T3_> class ntuple<T0_,T1_,T2_,T3_,false,false,false,false> { ... };

 template<typename T0_,
 typename T1_,
 typename T2_,
 typename T3_> class ntuple<T0_,T1_,T2_,T3_,true,false,false,false> { ... };

等。这会将部分特化的数量减少到与之前相同的数量,并且只需要为每个有效的“常量”类型专门化特征类。问题是,我想避免额外的模板参数。我可以通过继承,定义辅助类

来做到这一点
 template<typename T0_ = void,
 typename T1_ = void,
 typename T2_ = void,
 typename T3_ = void,
 bool const0 = is_const<T0_>::value,
 bool const1 = is_const<T1_>::value,
 bool const2 = is_const<T2_>::value,
 bool const3 = is_const<T3_>::value
 > class ntuple_impl;

如上所述,然后

 template <class T0, class T1, class T2, class T3>
 class ntuple  : ntuple_impl<T0, T1, T2, T3, 
                 is_const<T0>::value, 
                 is_const<T1>::value, 
                 is_const<T2>::value,
                 is_const<T3>::value> { ... };

但是我想避免继承,因为在某些情况下生成的对象会比必要的大,因为它将包含ntuple_impl作为子对象。我想知道是否有另一个解决这个问题的方法。谢谢。朱

1 个答案:

答案 0 :(得分:1)

即使这个问题超过一年,我觉得它应该得到正确答案。

遵循Xeo的想法,您可以以更简单的方式完成您想要的任务。 在这里,我假设你想要纯c ++ 03。

首先,声明一个仅用作占位符的类型

struct _;

还声明要解释为静态常量的类型。

template<typename T, T>
struct constant;

请注意,上述两种情况均无需定义。

下一步是定义代表元组元素的类型,即实际保存数据的类型。

template<typename T>
struct element
{
    typedef T type;
    type value;
};

template<>
struct element<_>;

关键点是为element以及您想要的其他任何类型(如您引用的constantint_专门化 bool_

template<typename T, T val>
struct element<constant<T, val> >
{
    typedef T const type;
    static type value = val;
};

template<typename T, T val>
typename element<constant<T, val> >::type element<constant<T, val> >::value;

从这一点来说,定义ntuple很简单。

template<typename T0_ = _, typename T1_ = _, typename T2_ = _, typename T3_ = _>
struct ntuple : element<T0_>, ntuple<T1_, T2_, T3_, _>
{
    typedef element<T0_> head;
    typedef ntuple<T1_, T2_, T3_, _> tail;
};

//nil
template<>
struct ntuple<_, _, _, _>
{};

要访问ntuple存储的元数据,可以使用元函数get_type

template<std::size_t n, typename T>
struct get_type;

template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
struct get_type<n, ntuple<T0, T1, T2, T3> > : get_type<n-1, typename ntuple<T0, T1, T2, T3>::tail>
{};

template<typename T0, typename T1, typename T2, typename T3>
struct get_type<0, ntuple<T0, T1, T2, T3> >
{
    typedef typename ntuple<T0, T1, T2, T3>::head::type type;
};

同样,要访问运行时数据,可以使用函数get_value

template<bool cond, typename T>
struct enable_if
{
};

template<typename T>
struct enable_if<true, T>
{
    typedef T type;
};

template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
typename enable_if<n, typename get_type<n, ntuple<T0, T1, T2, T3> >::type&>::type
get_value(ntuple<T0, T1, T2, T3>& tuple)
{
    return get_value<n-1>(static_cast<typename ntuple<T0, T1, T2, T3>::tail&>(tuple));
}

template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
typename enable_if<!n, typename ntuple<T0, T1, T2, T3>::head::type&>::type
get_value(ntuple<T0, T1, T2, T3>& tuple)
{
    return static_cast<typename ntuple<T0, T1, T2, T3>::head&>(tuple).value;
}

template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
typename enable_if<n, typename get_type<n, ntuple<T0, T1, T2, T3> >::type const&>::type
get_value(ntuple<T0, T1, T2, T3> const& tuple)
{
    return get_value<n-1>(static_cast<typename ntuple<T0, T1, T2, T3>::tail const&>(tuple));
}

template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
typename enable_if<!n, typename ntuple<T0, T1, T2, T3>::head::type const&>::type
get_value(ntuple<T0, T1, T2, T3> const& tuple)
{
    return static_cast<typename ntuple<T0, T1, T2, T3>::head const&>(tuple).value;
}

要计算元组中存储的元素数量,有一个名为get_size的函数

template<std::size_t n, typename T>
struct get_size_impl;

template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
struct get_size_impl<n, ntuple<T0, T1, T2, T3> > : 
        get_size_impl<n+1, typename ntuple<T0, T1, T2, T3>::tail>
{
};

template<std::size_t n>
struct get_size_impl<n, ntuple<_, _, _, _> >
{
    static std::size_t const value = n;
};

template<std::size_t n>
std::size_t const get_size_impl<n, ntuple<_, _, _, _> >::value;

template<typename T0, typename T1, typename T2, typename T3>
std::size_t get_size(ntuple<T0, T1, T2, T3> const&)
{
    return get_size_impl<0, ntuple<T0, T1, T2, T3> >::value;
}

最后是operator <<的重载用于打印目的

template<typename Char, typename CharTraits, typename T0, typename T1, typename T2, typename T3>
void print_element(std::basic_ostream<Char, CharTraits>& ostream, ntuple<T0, T1, T2, T3> const& tuple)
{
    ostream << static_cast<typename ntuple<T0, T1, T2, T3>::head const&>(tuple).value;
    if(get_size(tuple) > 1)
        ostream << std::basic_string<Char, CharTraits>(", ");

    print_element(ostream, static_cast<typename ntuple<T0, T1, T2, T3>::tail const&>(tuple));
}

template<typename Char, typename CharTraits>
void print_element(std::basic_ostream<Char, CharTraits>& ostream, ntuple<_, _, _, _> const&)
{

}

template<typename Char, typename CharTraits, typename T0, typename T1, typename T2, typename T3>
std::basic_ostream<Char, CharTraits>& operator <<(std::basic_ostream<Char, CharTraits>& ostream, ntuple<T0, T1, T2, T3> const& tuple)
{
    ostream << Char('<');
    print_element(ostream, tuple);
    ostream << Char('>');
}

以下用例说明了ntuples的使用,并明确了目标优化的实现。

int main()
{
    ntuple<char, int, long> a;
    ntuple<constant<char, '8'>, int, constant<long, 888> > b;
    ntuple<constant<char, '9'>, constant<int, 99>, constant<long, 999> > c;

    assert(sizeof(a) > sizeof(b));
    assert(sizeof(b) > sizeof(c));

    get_value<0>(a) = '1';
    get_value<1>(a) = 10;
    get_value<2>(a) = 100;
//    get_value<0>(b) = '2';    //assignment of read-only location
    get_value<1>(b) = 20;
//    get_value<2>(b) = 200;    //assignment of read-only location
//    get_value<0>(c) = '3';    //assignment of read-only location
//    get_value<1>(c) = 30;     //assignment of read-only location
//    get_value<2>(c) = 300;    //assignment of read-only location

    std::cout << std::endl;
    std::cout << "a = " << a << ", length: " << get_size(a) << ", size in bytes: " << sizeof(a) << std::endl;
    std::cout << "b = " << b << ", length: " << get_size(b) << ", size in bytes: " << sizeof(b) << std::endl;
    std::cout << "c = " << c << ", length: " << get_size(c) << ", size in bytes: " << sizeof(c) << std::endl;
}

这个简单的案例涵盖了最多包含4个元素的ntuples。应该可以直接将它扩展到所需数量的元素(并且由所使用的编译器支持)。实际上,使用这种方法生成代码不需要任何脚本。

相关问题