2010-07-21 29 views
9

Có trường hợp khi nguồn thư viện khả dụng và nó phải hỗ trợ các tham số biến nói chung, nhưng trong thực tế, các tham số này thường là hằng số. Sau đó, có thể tối ưu hóa mọi thứ bằng cách xử lý đặc biệt các tham số không đổi (ví dụ: sử dụng các mảng tĩnh thay vì phân bổ đống), nhưng cần thiết để xác định xem có gì không phải là đầu tiên hay không (hoặc có thể xác định một số macro). nhưng ít thuận tiện hơn).Phát hiện liên tục biên dịch C++

Vì vậy, đây là triển khai hoạt động.

Cập nhật: cũng ở đây: http://codepad.org/ngP7Kt1V

  1. Có thực sự là một C++ hợp lệ?
  2. Có cách nào để loại bỏ các macro này không? (is_const() không thể là hàm bởi vì sự phụ thuộc hàm sẽ không hoạt động trong biểu thức kích thước mảng, cũng không thể là mẫu vì nó cũng không chấp nhận tham số biến.)

Cập nhật: Đây là bản cập nhật với nội dung nào đó giống như mục đích sử dụng. Trình biên dịch sẽ không tạo ra bất kỳ mã nào cho chi nhánh if(N==0) nếu N không phải là 0. Cùng một cách chúng ta có thể chuyển sang cấu trúc dữ liệu hoàn toàn khác nếu chúng ta muốn. Chắc chắn nó không hoàn hảo, nhưng đó là lý do tại sao tôi đăng câu hỏi này.


#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()' làm việc cho x> = 0 chỉ, tuy nhiên mẹo (tạo kết quả biên dịch không xác định) hoạt động với 'is_const (X) | is_const (-X) 'quá, do đó có is_const chỉ làm việc cho' tất cả x: x! = INT_MIN'. –

+0

Lưu ý rằng 'sizeof (int)' và 'sizeof (char)' không được bảo đảm khác (và có các bộ xử lý thực tế trong đó chúng giống nhau), vì vậy bạn nên sử dụng cái gì đó như 'char [2]'. (Mặt khác, tôi thấy các hằng số được mã hóa cứng nên tôi cho rằng tính di động không phải là một mối quan tâm.) – ymett

+0

Mã tuyệt vời, ý tưởng tuyệt vời (tôi đoán nguồn gốc là http://encode.ru/threads/396-C-compile-time -constant-detection?) Tôi đã điều chỉnh mã is_const để có thể di chuyển hơn một chút (các vấn đề về sizeof char, INT_MAX được sử dụng), để xử lý tất cả các giá trị đầu vào có thể, và tạo ra một phiên bản không gcc đơn giản hơn - xem http://stackoverflow.com/questions/7658060/ có thể-i-sử dụng-giả-gơ-để-elide-a-gọi-nếu-một-điều kiện-được-biết-tại-biên dịch/7658363 # 7658363 – Suma

Trả lời

0

Nếu bạn có thể vượt qua trong một mẫu tham số sau đó nó được đảm bảo để trở thành một constexpr (thuật ngữ của Tiêu chuẩn cho các biểu thức thời gian biên dịch). Nếu nó không được thông qua bởi tham số mẫu, thì nó không phải là một constexpr. Không có cách nào xung quanh chuyện này.

Điều gì sẽ dễ dàng hơn nhiều khi cuộn tay một lớp mảng có độ dài biến được phân bổ bằng cách sử dụng alloca. Điều này sẽ đảm bảo phân bổ stack cho mảng, bất kể chúng có tĩnh hay không. Ngoài ra, bạn có thể nhận được nhiều chức năng lặp lại giống nhau của mảng vectơ/boost ::.

 #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]; 
      } 
     }; 

Lưu ý rằng tôi chưa thực sự thử nghiệm mã này, nhưng sẽ dễ dàng hơn để nắm bắt, sử dụng và duy trì hơn để duy trì tính quái dị đó trong OP của bạn.

+0

Vâng, mã của tôi thực hiện một cách để chuyển một hàm (có thể) biến làm thông số mẫu. Ngoài ra, điều này không thực sự về việc phân bổ các bảng trên stack, nhưng về việc chuyển đổi sang một phiên bản thư viện được tối ưu hóa khi nó xuất hiện (có thể một số tham số) được biết tại thời gian biên dịch. – Shelwien

+0

@Shelwien: Đúng là mã của bạn có một số tiềm năng mà tôi không có. Tuy nhiên, mã của tôi cũng có nhiều tiềm năng mà bạn không có. – Puppy

+0

Chắc chắn, cảm ơn bạn đã chia sẻ mã của bạn, nhưng không thực sự liên quan đến chủ đề này :) – Shelwien

1

is_const phải đáng tin cậy hơn. Trên gcc-4.4 ví dụ như sau:

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

in:

0,1 

GCC là khá tham vọng biểu gấp liên tục mà không phải là không thể thiếu biểu thức liên tục bởi những lời của tiêu chuẩn. Một định nghĩa có khả năng tốt hơn về is_const có thể là:

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

Bên cạnh đó, kỹ thuật của bạn là tuyệt vời, bởi vì cuối cùng tôi có thể viết một macro SUPER_ASSERT được kiểm tra trong quá trình biên soạn, nếu biểu thức khẳng định nếu thời gian biên dịch và trong thời gian chạy bằng cách khác :

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

Tôi sẽ xem xét điều đó sau const_switch_xxx() sau.Tôi không có ý tưởng làm thế nào để thực hiện một cách khác, lừa deconstruct/tái tạo là rực rỡ.

+1

Đây có phải là sự vi phạm tiêu chuẩn của gcc không? Tôi không thể tìm thấy bất kỳ từ ngữ nào cho phép nó diễn giải ví dụ. 'k-k' (trong đó' k' là một biến kiểu 'unsigned int') như là một biểu thức hằng số tích phân và do đó là hằng số null-pointer, mặc dù nó luôn luôn là 0. – jpalecek

+0

Lưu ý: vì bạn quan tâm đến việc phát hiện thời gian biên dịch bằng không, việc thực hiện SUPER_ASSERT có thể đơn giản hơn nhiều, không có const_switch và bất kỳ vấn đề tương thích gcc nào: # define SUPER_ASSERT (X) {BOOST_STATIC_ASSERT (! Is_const_0 (X)); ;} – Suma

1

Nếu bạn đang làm việc với GCC, hãy sử dụng __builtin_constant_p để cho bạn biết liệu có điều gì đó là thời gian biên dịch không. Tài liệu bao gồm các ví dụ như

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

Tôi đã thử điều đó, nhưng khác biệt, giống như mẫu tăng tương ứng - ví dụ của tôi không hoạt động nếu tôi xác định lại is_const 0 và is_const_0i sử dụng kết quả được xây dựng - const_switch * sai và ví dụ mẫu không biên dịch. Ngoài ra tôi cần nó tương thích với MSC và IntelC, bên cạnh gcc. – Shelwien

Các vấn đề liên quan