Обнаружение константы времени компиляции C ++

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

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

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

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

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

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


 #include <stdio.h>

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 ); }

person Shelwien    schedule 21.07.2010    source источник
comment
is_const() работает только для x ›= 0, однако уловка (сделать результат неопределенным во время компиляции) работает и с is_const(X) | is_const(-X), поэтому is_const работает только для all x: x!=INT_MIN.   -  person Nordic Mainframe    schedule 21.07.2010
comment
Обратите внимание, что sizeof(int) и sizeof(char) не обязательно будут разными (и есть реальные процессоры, где они одинаковы), поэтому вам следует использовать что-то вроде char[2]. (С другой стороны, я вижу жестко запрограммированные константы, поэтому полагаю, что переносимость не вызывает беспокойства.)   -  person ymett    schedule 04.08.2011
comment
Отличный код, отличная идея (полагаю, исходный источник - encode.ru / thread / 396-C-compile-time-constant-detection?). Я адаптировал код is_const, чтобы он был немного более портативным (проблемы с sizeof char, используется INT_MAX), для обработки всех возможных входных значений и создал более простую версию, отличную от gcc - см. stackoverflow.com/questions/7658060/   -  person Suma    schedule 06.10.2011


Ответы (3)


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

static const int table[] = {
  __builtin_constant_p (EXPRESSION) ? (EXPRESSION) : -1,
  /* ... */
};
person Kirk Kelsey    schedule 04.08.2010
comment
Я пробовал это, но он отличается, как и соответствующий шаблон повышения - мой пример не работает, если я переопределю is_const 0 и is_const_0i, используя этот встроенный - результаты const_switch * неверны, а пример шаблона вообще не компилируется. Также мне нужно, чтобы он был совместим с MSC и IntelC, помимо gcc. - person Shelwien; 05.08.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 () позже. Я понятия не имею, как реализовать другой способ, трюк деконструировать / реконструировать великолепен.

person Nordic Mainframe    schedule 21.07.2010
comment
Разве это не нарушение стандарта gcc? Я не могу найти формулировку, которая позволила бы его интерпретировать, например. k-k (где k - это переменная типа unsigned int) как интегральное выражение-константа и, следовательно, как константа с нулевым указателем, даже если она всегда равна нулю. - person jpalecek; 04.08.2010
comment
Примечание: поскольку вас беспокоит определение только нулевого времени компиляции, реализация SUPER_ASSERT может быть намного проще, без const_switch и каких-либо проблем совместимости с gcc: #define SUPER_ASSERT (X) {BOOST_STATIC_ASSERT (! Is_const_0 (X)); assert (X); } - person Suma; 13.10.2011

Если вы можете передать параметр шаблона, то это гарантированно будет 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.

person Puppy    schedule 21.07.2010
comment
Что ж, мой код реализует способ передачи функции (возможной) переменной в качестве параметра шаблона. Кроме того, речь идет не о размещении таблиц в стеке, а о переключении на оптимизированную версию библиотеки, когда выясняется, что (возможно, некоторые из) параметры известны во время компиляции. - person Shelwien; 21.07.2010
comment
@Shelwien: Это правда, что ваш код имеет некоторый потенциал, чем мой. Однако мой код также имеет большой потенциал, которого нет у вас. - person Puppy; 21.07.2010
comment
Конечно, спасибо, что поделились своим кодом, но, по-моему, это не совсем связано с темой :) - person Shelwien; 21.07.2010
comment
@Shelwien: он решает актуальную проблему, которую вы опубликовали (статическое и динамическое распределение потенциально определяющего размера времени выполнения). Я только что создал другую (намного лучшую) реализацию с другой идеей поддержки относительно того, как этого добиться. - person Puppy; 21.07.2010
comment
-1: OP не хочет решать некоторые массивы переменной длины в стеке. Он показал нам метод определения во время компиляции, является ли значение constexpr , и гениальный способ создания constexpr для выбора constexpr в зависимости от constexpr'ness другого значения. Прежде чем проголосовать за что-либо, вы должны понять, что это значит. Значение const_switch_uint можно использовать везде, где требуется постоянное целое значение, например, параметры, не относящиеся к типу, определения перечислений, метки case операторов switch ... - person Nordic Mainframe; 22.07.2010
comment
@ Лютер: Нет, ты должен научиться читать. Он сам говорит, что вы не можете проделать этот трюк с параметрами функции, поэтому ровно ничего не решите. Утверждение в моем OP полностью истинно - значения constexpr для параметров функции не создают аргумент constexpr. OP должен будет отдельно предоставить как шаблон, так и версии аргументов времени выполнения любой библиотеки, которая желает это поддерживать. Если он хочет использовать предоставленный пользователем int для создания экземпляров шаблонов, ему придется либо начать включать определяемые пользователем заголовки, либо брать их по шаблону. Его макросы не решают эту проблему. - person Puppy; 22.07.2010
comment
@Luther Blissett: Согласен, хотя магия в реализации для меня слишком злая :) пытаюсь найти более простой способ, поскольку доказано, что это возможно - person Alsk; 22.07.2010
comment
@DeadMG: или любая библиотечная функция будет иметь один и тот же аргумент int в двух формах - форма аргумента шаблона и обычный аргумент функции, и каждый вызов будет заключен в макрос, как это уже показано Шелвином. - person Alsk; 22.07.2010