Обнаружение констант во время компиляции C ++ - PullRequest
9 голосов
/ 21 июля 2010

Есть случаи, когда источник библиотеки доступен, и он должен поддерживать переменные параметры в целом, но на практике эти параметры обычно являются константами.

Тогда может быть возможно оптимизировать вещи с помощью специальной обработкипостоянных параметров (например, использовать статические массивы вместо распределения кучи), но для этого необходимо сначала определить, является ли что-то постоянным (или, возможно, определить некоторые макросы, но это менее удобно).

Так вотрабочая реализация.

Обновление: также здесь: http://codepad.org/ngP7Kt1V

  1. Действительно ли это допустимый C ++?
  2. Есть ли способ избавиться от этих макросов?(is_const () не может быть функцией, потому что зависимость функции не будет работать в выражении размера массива; также он не может быть шаблоном, потому что он также не будет принимать переменный параметр.)

Обновление: Вот обновление с чем-то вроде использования по назначению.Компилятор не будет генерировать никакого кода для ветви if(N==0), если N не равен 0. Точно так же мы можем переключиться на совершенно разные структуры данных, если захотим.Конечно, это не идеально, но вот почему я написал этот вопрос.


 #include <stdio.h></p>

struct chkconst {
  struct Temp { Temp( int x ) {} };
  static char chk2( void* ) { return 0; }
  static int  chk2( Temp  ) { return 0; }
};

#define is_const_0(X) (sizeof(chkconst::chk2(X))<sizeof(int))
#define is_const_0i(X) (sizeof(chkconst::chk2(X))>sizeof(char))
#define is_const(X) is_const_0( (X)^((X)&0x7FFFFFFF) )

#define const_bit(X1,bit) (is_const_0i((X1)&(1<<bit))<<bit)
#define const_nibl(X1,bit) const_bit(X1,bit) | const_bit(X1,(bit+1)) | const_bit(X1,(bit+2)) | const_bit(X1,(bit+3)) 
#define const_byte(X1,bit) const_nibl(X1,bit) | const_nibl(X1,(bit+4))
#define const_word(X1,bit) const_byte(X1,bit) | const_byte(X1,(bit+8))
#define const_uint(X1) const_word(X1,0) | const_word(X1,16)
#define const_switch_word( X1, X2 ) (is_const(X1) ? const_word(X1,0) : X2)
#define const_switch_uint( X1, X2 ) (is_const(X1) ? const_uint(X1) : X2)

const int X1 = 222;
const int X2 = printf( "" ) + 333;

char Y1[ const_switch_word(X1,256) ];
char Y2[ const_switch_word(X2,256) ];

template< int N > 
void test( int N1 ) {
  char _buf[N>0?N:1];
  char* buf = _buf;
  if( N==0 ) {
    buf = new char[N1];
  }
  printf( "%08X %3i %3i\n", buf, N, N1 );
}

#define testwrap(N) test< const_switch_word(N,0) >( N )

int main( void ) {
  printf( "%i %i %i\n", X1, is_const(X1), sizeof(Y1) );
  printf( "%i %i %i\n", X2, is_const(X2), sizeof(Y2) );
  testwrap( X1 );
  testwrap( X2 );
}

Ответы [ 3 ]

2 голосов
/ 05 августа 2010

Если вы работаете с GCC, используйте __builtin_constant_p, чтобы сообщить вам, является ли что-то постоянным времени компиляции. Документация включает в себя примеры, такие как

static const int table[] = {
  __builtin_constant_p (EXPRESSION) ? (EXPRESSION) : -1,
  /* ... */
};
1 голос
/ 21 июля 2010

is_const должен быть более надежным. На gcc-4.4, например, следующее:

int k=0;
printf("%d\n",is_const(k),is_const(k>0));

отпечатков:

0,1

GCC - это довольно амбициозное сворачивание константных выражений, которые не являются целочисленными константными выражениями по словам стандарта. Потенциально лучшим определением is_const может быть:

#define is_const(B)\
(sizeof(chkconst::chk2(0+!!(B))) != sizeof(chkconst::chk2(0+!(B))))

Кроме того, ваша техника великолепна, потому что я могу наконец написать макрос SUPER_ASSERT, который проверяется во время компиляции, если выражение утверждения, если во время компиляции, и во время выполнения в противном случае:

#define SUPER_ASSERT(X) {BOOST_STATIC_ASSERT(const_switch_uint(X,1));assert(X);}

Я расскажу об этом const_switch_xxx () позже. Я понятия не имею, как реализовать другой способ, трюк деконструкции / реконструкции блестящий.

0 голосов
/ 21 июля 2010

Если вы можете передать параметр шаблона, то он гарантированно будет constexpr (термин стандарта для выражений времени компиляции). Если это не передано параметром шаблона, то это не constexpr. Обойти это невозможно.

Что было бы намного проще, так это прокрутить класс массива переменной длины, выделенный стеком, используя alloca. Это гарантирует выделение стека для массивов, независимо от того, являются они статическими или нет. Кроме того, вы можете получить большую часть той же функциональности итераций в vector / boost :: array.

        #define MAKE_VLA(type, identifier, size) VLA< (type) > identifier ( alloca( (size) * sizeof ( type ) ), (size) );
        template<typename T> class VLA {
            int count;
            T* memory;
            VLA(const VLA& other);
        public:
            // Types
            typedef T* pointer;
            typedef T& reference;
            typedef const T* const_pointer;
            typedef const T& const_reference;
            typedef T value_type;
            typedef std::size_t size_type;
            class iterator {
                mutable T* ptr;
                iterator(T* newptr)
                    : ptr(newptr) {}
            public:
                iterator(const iterator& ref)
                    : ptr(ref.ptr) {}

                operator pointer() { return ptr; }
                operator const pointer() const { return ptr; }

                reference operator*() { return *ptr; }
                const reference operator*() const { return *ptr; }

                pointer operator->() { return ptr; }
                const pointer operator->() const { return ptr; }

                iterator& operator=(const iterator& other) const {
                    ptr = iterator.ptr;
                }

                bool operator==(const iterator& other) {
                    return ptr == other.ptr;
                }
                bool operator!=(const iterator& other) {
                    return ptr != other.ptr;
                }

                iterator& operator++() const {
                    ptr++;
                    return *this;
                }
                iterator operator++(int) const {
                    iterator retval(ptr);
                    ptr++;
                    return retval;
                }
                iterator& operator--() const {
                    ptr--;
                    return *this;
                }
                iterator operator--(int) const {
                    iterator retval(ptr);
                    ptr--;
                    return retval;
                }

                iterator operator+(int x) const {
                    return iterator(&ptr[x]);
                }
                iterator operator-(int x) const {
                    return iterator(&ptr[-x]);
                }
            };
            typedef const iterator const_iterator;
            class reverse_iterator {
                mutable T* ptr;
                reverse_iterator(T* newptr)
                    : ptr(newptr) {}
            public:
                reverse_iterator(const reverse_iterator& ref)
                    : ptr(ref.ptr) {}

                operator pointer() { return ptr; }
                operator const pointer() const { return ptr; }

                reference operator*() { return *ptr; }
                const reference operator*() const { return *ptr; }

                pointer operator->() { return ptr; }
                const pointer operator->() const { return ptr; }

                reverse_iterator& operator=(const reverse_iterator& other) const {
                    ptr = reverse_iterator.ptr;
                }
                bool operator==(const reverse_iterator& other) {
                    return ptr == other.ptr;
                }
                bool operator!=(const reverse_iterator& other) {
                    return ptr != other.ptr;
                }

                reverse_iterator& operator++() const {
                    ptr--;
                    return *this;
                }
                reverse_iterator operator++(int) const {
                    reverse_iterator retval(ptr);
                    ptr--;
                    return retval;
                }
                reverse_iterator& operator--() const {
                    ptr++;
                    return *this;
                }
                reverse_iterator operator--(int) const {
                    reverse_iterator retval(ptr);
                    ptr++;
                    return retval;
                }

                reverse_iterator operator+(int x) const {
                    return reverse_iterator(&ptr[-x]);
                }
                reverse_iterator operator-(int x) const {
                    return reverse_iterator(&ptr[x]);
                }
            };
            typedef const reverse_iterator const_reverse_iterator;
            typedef unsigned int difference_type;

            // Functions
            ~VLA() {
                for(int i = 0; i < count; i++)
                    memory[i].~T();
            }
            VLA(void* stackmemory, int size)
                : memory((T*)stackmemory), count(size) {
                    for(int i = 0; i < count; i++)
                        new (&memory[i]) T();
            }

            reference at(size_type pos) {
                return (reference)memory[pos];
            }
            const_reference at(size_type pos) {
                return (const reference)memory[pos];
            }
            reference back() {
                return (reference)memory[count - 1];
            }
            const_reference back() const {
                return (const reference)memory[count - 1];
            }

            iterator begin() {
                return iterator(memory);
            }
            const_iterator begin() const {
                return iterator(memory);
            }

            const_iterator cbegin() const {
                return begin();
            }

            const_iterator cend() const {
                return end();
            }

            const_reverse_iterator crbegin() const {
                return rbegin();
            }

            const_reverse_iterator crend() const {
                return rend();
            }

            pointer data() {
                return memory;
            }
            const_pointer data() const { 
                return memory;
            }

            iterator end() {
                return iterator(&memory[count]);
            }
            const_iterator end() const {
                return iterator(&memory[count]);
            }

            reference front() {
                return memory[0];
            }
            const_reference front() const {
                return memory[0];
            }

            reverse_iterator rbegin() {
                return reverse_iterator(&memory[count - 1]);
            }
            const_reverse_iterator rbegin() const {
                return const_reverse_iterator(&memory[count - 1]);
            }
            reverse_iterator rend() {
                return reverse_iterator(memory[-1]);
            }
            const_reverse_iterator rend() const {
                return reverse_iterator(memory[-1]);
            }

            size_type size() {
                return count;
            }

            reference operator[](int index) {
                return memory[index];
            }
            const reference operator[](int index) const {
                return memory[index];
            }
        };

Обратите внимание, что я на самом деле не тестировал этот код, но было бы НАМНОГО легче получить, использовать и поддерживать, чем поддерживать это чудовище в вашем OP.

...