2010-07-21 1 views
9

Есть случаи, когда доступен источник библиотеки, и он должен поддерживать переменные параметры в целом, но на практике эти параметры обычно являются константами.Обнаружение постоянной времени компиляции 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); }
+0

'is_const()' работает при х> = 0 только, однако трюк (делают результат во время компиляции не определено) работает с 'is_const (X) | is_const (-X) ', так что is_const работает только для' all x: x! = INT_MIN'. –

+0

Обратите внимание, что 'sizeof (int)' и 'sizeof (char)' не гарантируется быть разными (и есть реальные процессоры, где они одинаковые), поэтому вы должны использовать что-то вроде 'char [2]'. (С другой стороны, я вижу жестко закодированные константы, поэтому я полагаю, что переносимость не вызывает беспокойства.) – ymett

+0

Отличный код, отличная идея (я думаю, исходный источник http://encode.ru/threads/396-C-compile-time -константа обнаружения?). Я адаптировал код is_const, чтобы быть немного более переносимым (sizeof char issues, INT_MAX), чтобы обрабатывать все возможные входные значения и создал более простую версию без gcc - см. Http://stackoverflow.com/questions/7658060/ can-i-use-accept-hint-to-elide-a-call-if-an-edge-condition-is-known-at-compile/7658363 # 7658363 – Suma

ответ

0

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

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

+0

Ну, мой код реализует способ передачи функции (возможной) переменной в качестве параметра шаблона. Также речь идет не о распределении таблиц в стеке, а о переключении на оптимизированную версию библиотеки, когда появляется, что (возможно, некоторые из) параметров известны во время компиляции. – Shelwien

+0

@Shelwien: Это правда, что у вашего кода есть потенциал, который у меня нет. Тем не менее, мой код также имеет большой потенциал, которого у вас нет. – Puppy

+0

Несомненно, спасибо за обмен кодом, но imho его действительно не связано с темой :) – Shelwien

1

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

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

печатает:

0,1 

НКА весьма честолюбивые складной константные выражения, которые не являются неотъемлемыми константными выражениями слов стандарта. Потенциально более четкое определение 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().Я понятия не имею, как реализовать другой путь, деконструкция/реконструкция трюка блестящая.

+1

. Разве это не является нарушением стандарта gcc? Я не могу найти какой-либо формулировки, которая позволила бы ее интерпретировать, например. 'k-k' (где' k' - это переменная типа unsigned int') как целое константное выражение и, следовательно, как константа нулевого указателя, даже если она всегда оценивает ноль. – jpalecek

+0

Примечание: поскольку вы обеспокоены только обнаружением нуля только для компиляции, реализация SUPER_ASSERT может быть намного проще, без ошибок const_switch и gcc: #define SUPER_ASSERT (X) {BOOST_STATIC_ASSERT (! Is_const_0 (X)); assert (X) ;} – Suma

1

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

static const int table[] = { 
    __builtin_constant_p (EXPRESSION) ? (EXPRESSION) : -1, 
    /* ... */ 
}; 
+0

Я пробовал это, но его разные, то же, что и соответствующий шаблон boost - мой пример не работает, если я переопределяю is_const 0 и is_const_0i, используя этот результат builtin-const_switch *, неверны, и пример шаблона вообще не компилируется. Также мне нужно, чтобы он был совместим с MSC и IntelC в любом случае, рядом с gcc. – Shelwien