2012-03-02 5 views
0

Я пытаюсь реализовать класс типа std :: pair, но с более чем двумя компонентами. Поскольку в моем приложении может случиться, что некоторые компоненты кортежа уже известны во время компиляции, я бы хотел иметь следующую оптимизацию пространства: когда я знаю, что компонент является константой времени компиляции, просто объявите его как " static 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; 

Таким образом, члены, помеченные как постоянные <.,.> Не сохраняются как члены класса, что уменьшает размер объекта. Количество требуемой частичной специализации может быть огромным, 2^N для N = 1,2,3,4 i сообщать только до 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 в качестве под-объекта. Я бы знал, есть ли другое решение этой проблемы. Благодарю. Giuliano

+2

Ваши подчеркивания сводят меня с ума. Кроме того, уверены ли вы, что это хороший способ оптимизации? Я думаю, что вам не хватает всего шаблона, если вам нужен скрипт для их создания! – vulkanino

+0

Поскольку мне нужно поставить ключевое слово «статический» перед выбранными членами данных, а «статический» не является частью этого типа, для достижения этого невозможно использовать шаблонные трюки. По крайней мере, я думаю ... Спасибо! – Giuliano

+0

Я думаю, вы должны попытаться описать то, что вы хотите достичь, потому что я думаю, что очень возможно, что есть намного более простой способ. –

ответ

1

Несмотря на то, что этот вопрос старше года, я считаю, что он заслуживает правильного ответа.

Следуя идее Xeo, вы можете сделать именно то, что хотите, намного проще. Здесь я предполагаю, что вы хотите чистый C++ 03.

Прежде всего, объявить тип должен использоваться только в качестве заполнителя

struct _; 

Declare также типа, который будет интерпретироваться как статическая константа.

template<typename T, T> 
struct constant; 

Отметьте, что для любого из указанных выше определения не требуется.

Следующий шаг - определить тип, который будет представлять собой элемент кортежа, то есть тип, который фактически хранит данные.

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

template<> 
struct element<_>; 

Ключевым моментом является то, чтобы специализироватьсяelement для constant и в зависимости от того других типов, которые вы хотите, как int_ и 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, один использует metafunction 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; 
} 

Этот простой чехол охватывает ntuples, который имеет максимум 4 элемента. Это должно быть просто, чтобы расширить его до такого количества элементов, как требуется (и поддерживается используемым компилятором).На самом деле для генерации кода с использованием этого подхода не требуется никакого сценария.

Смежные вопросы