2010-09-30 11 views
6

Tôi có một bộ cờ bit được sử dụng trong chương trình tôi đang chuyển từ C sang C++.Tại sao C++ hỗ trợ chuyển nhượng Hex, nhưng thiếu phân bổ nhị phân? Làm thế nào tốt nhất để lưu trữ cờ?

Để bắt đầu ...

Các cờ trong chương trình của tôi trước đây được định nghĩa là:

/* Define feature flags for this DCD file */ 
#define DCD_IS_CHARMM  0x01 
#define DCD_HAS_4DIMS  0x02 
#define DCD_HAS_EXTRA_BLOCK 0x04 

... Bây giờ tôi đã thu thập rằng # định nghĩa cho các hằng số (so với hằng số lớp vv) thường được coi là hình thức xấu.

Điều này đặt ra câu hỏi về cách lưu trữ cờ bit tốt nhất trong C++ và tại sao C++ không hỗ trợ gán văn bản nhị phân cho int, như cho phép số hex được gán theo cách này (thông qua "0x"). Những câu hỏi này được tóm tắt ở cuối bài đăng này.

tôi có thể thấy một giải pháp đơn giản là chỉ cần tạo ra hằng cá nhân:

namespace DCD { 
    const unsigned int IS_CHARMM = 1; 
    const unsigned int HAS_4DIMS = 2; 
    const unsigned int HAS_EXTRA_BLOCK = 4; 
}; 

Hãy gọi ý tưởng này 1.

Một ý tưởng tôi đã có được sử dụng một enum số nguyên:

namespace DCD { 
    enum e_Feature_Flags { 
     IS_CHARMM = 1, 
     HAS_4DIMS = 2, 
     HAS_EXTRA_BLOCK = 8 
    }; 
}; 

Nhưng một điều làm phiền tôi về điều này là nó ít trực quan hơn khi nói đến các giá trị cao hơn, có vẻ như ... tức là

namespace DCD { 
    enum e_Feature_Flags { 
     IS_CHARMM = 1, 
     HAS_4DIMS = 2, 
     HAS_EXTRA_BLOCK = 8, 
     NEW_FLAG = 16, 
     NEW_FLAG_2 = 32, 
     NEW_FLAG_3 = 64, 
     NEW_FLAG_4 = 128 
    }; 
}; 

Hãy gọi lựa chọn phương pháp này 2.

tôi đang xem xét sử dụng giải pháp vĩ mô Tom Torf của:

#define B8(x) ((int) B8_(0x##x)) 

#define B8_(x) \ 
(((x) & 0xF0000000) >(28 - 7) \ 
| ((x) & 0x0F000000) >(24 - 6) \ 
| ((x) & 0x00F00000) >(20 - 5) \ 
| ((x) & 0x000F0000) >(16 - 4) \ 
| ((x) & 0x0000F000) >(12 - 3) \ 
| ((x) & 0x00000F00) >(8 - 2) \ 
| ((x) & 0x000000F0) >(4 - 1) \ 
| ((x) & 0x0000000F) >(0 - 0)) 

chuyển đổi sang nội tuyến chức năng, ví dụ

#include <iostream> 
#include <string> 
.... 

/* TAKEN FROM THE C++ LITE FAQ [39.2]... */ 
class BadConversion : public std::runtime_error { 
public: 
    BadConversion(std::string const& s) 
    : std::runtime_error(s) 
    { } 
}; 

inline double convertToUI(std::string const& s) 
{ 
    std::istringstream i(s); 
    unsigned int x; 
    if (!(i >> x)) 
    throw BadConversion("convertToUI(\"" + s + "\")"); 
    return x; 
} 
/** END CODE **/ 

inline unsigned int B8(std::string x) { 
    unsigned int my_val = convertToUI(x.insert(0,"0x").c_str()); 
    return ((my_val) & 0xF0000000) >(28 - 7) | 
      ((my_val) & 0x0F000000) >(24 - 6) | 
      ((my_val) & 0x00F00000) >(20 - 5) | 
      ((my_val) & 0x000F0000) >(16 - 4) | 
      ((my_val) & 0x0000F000) >(12 - 3) | 
      ((my_val) & 0x00000F00) >(8 - 2) | 
      ((my_val) & 0x000000F0) >(4 - 1) | 
      ((my_val) & 0x0000000F) >(0 - 0); 
} 

namespace DCD { 
    enum e_Feature_Flags { 
     IS_CHARMM  = B8("00000001"), 
     HAS_4DIMS  = B8("00000010"), 
     HAS_EXTRA_BLOCK = B8("00000100"), 
     NEW_FLAG  = B8("00001000"), 
     NEW_FLAG_2  = B8("00010000"), 
     NEW_FLAG_3  = B8("00100000"), 
     NEW_FLAG_4  = B8("01000000") 
    }; 
}; 

Điều này có điên không? Hay nó có vẻ trực quan hơn? Hãy gọi sự lựa chọn này 3.

Vì vậy, để tóm tắt, câu hỏi quá cong của tôi là:

1. Tại sao không phải C++ hỗ trợ một "0b" giá trị cờ, tương tự như "0x"?
2. Cách tốt nhất để xác định cờ ...
i. Không gian tên được bao bọc.
ii. Không gian tên được bao bọc bởi các int không dấu được gán trực tiếp.
iii. Không gian tên được bao bọc bởi các int không dấu được gán bằng chuỗi nhị phân có thể đọc được.

Cảm ơn trước! Và xin đừng đóng chủ đề này là chủ quan, bởi vì tôi thực sự muốn nhận được sự giúp đỡ về phong cách tốt nhất là gì và tại sao C++ thiếu khả năng phân công nhị phân.


EDIT 1

Một chút thông tin bổ sung. Tôi sẽ đọc một bitfield 32 bit từ một tập tin và sau đó kiểm tra nó với những lá cờ này. Vì vậy, hãy nhớ điều đó khi bạn gửi đề xuất.

Trả lời

13

Trước C++ 14, các số nhị phân đã được thảo luận và trong nhiều năm, nhưng theo như tôi biết, không ai từng viết một đề xuất nghiêm túc để đưa nó vào tiêu chuẩn, vì vậy nó không bao giờ thực sự có qua giai đoạn nói về nó. Đối với C++ 14, ai đó cuối cùng đã viết đề xuất và ủy ban chấp nhận đề xuất, vì vậy nếu bạn có thể sử dụng trình biên dịch hiện tại, tiền đề cơ bản của câu hỏi là sai - bạn có thể sử dụng các ký tự nhị phân, 0b01010101. Trong C++ 11, thay vì thêm trực tiếp các chữ nhị phân, chúng bổ sung thêm một cơ chế tổng quát hơn để cho phép các ký tự chung do người dùng định nghĩa, mà bạn có thể sử dụng để hỗ trợ nhị phân, hoặc 64 cơ sở hoặc các thứ khác hoàn toàn. . Ý tưởng cơ bản là bạn chỉ định một số (hoặc chuỗi) theo sau là hậu tố và bạn có thể xác định hàm sẽ nhận được chữ đó và chuyển đổi nó thành bất kỳ biểu mẫu nào bạn thích (và bạn có thể duy trì trạng thái của nó dưới dạng "hằng số "quá ...)

Để sử dụng: nếu bạn có thể, các chữ cái nhị phân được tích hợp trong C++ 14 hoặc cao hơn là lựa chọn hiển nhiên. Nếu bạn không thể sử dụng chúng, tôi muốn thường thích một biến thể của phương án 2: một enum với initializers trong hex:

namespace DCD { 
    enum e_Feature_Flags { 
     IS_CHARMM = 0x1, 
     HAS_4DIMS = 0x2, 
     HAS_EXTRA_BLOCK = 0x8, 
     NEW_FLAG = 0x10, 
     NEW_FLAG_2 = 0x20, 
     NEW_FLAG_3 = 0x40, 
     NEW_FLAG_4 = 0x80 
    }; 
}; 

Một khả năng khác là một cái gì đó như:

#define bit(n) (1<<(n)) 

enum e_feature_flags = { 
    IS_CHARM = bit(0), 
    HAS_4DIMS = bit(1), 
    HAS_EXTRA_BLOCK = bit(3), 
    NEW_FLAG = bit(4), 
    NEW_FLAG_2 = bit(5), 
    NEW_FLAG_3 = bit(6), 
    NEW_FLAG_4 = bit(7) 
}; 
+0

Ý bạn là 'int bit unsigned int (n) {1 << (n);}' phải không? ;) –

+1

@ Jason: Không; người khởi tạo điều tra viên phải là biểu thức không đổi; bạn không thể gọi một hàm trong một biểu thức liên tục, vì vậy nó phải là một macro. –

+0

hrmm ... Tôi đoán đó là thời điểm tốt để sử dụng macro? –

0

Tôi đoán điểm mấu chốt là nó không thực sự cần thiết.

Nếu bạn chỉ muốn sử dụng nhị phân cho cờ, cách tiếp cận dưới đây là cách tôi thường làm điều đó. Sau khi ban đầu xác định bạn không bao giờ phải lo lắng về nhìn vào "Messier" bội lớn hơn 2 như bạn đề cập

int FLAG_1 = 1 
int FLAG_2 = 2 
int FLAG_3 = 4 
... 
int FLAG_N = 256 

bạn có thể dễ dàng kiểm tra chúng với

if(someVariable & FLAG_3 == FLAG_3) { 
    // the flag is set 
} 

Và btw, Tùy thuộc vào bạn trình biên dịch (tôi đang sử dụng trình biên dịch GNU GCC) có thể hỗ trợ "0b"

lưu ý Đã chỉnh sửa để trả lời câu hỏi.

+0

Tuy nhiên, đây là một tiện ích mở rộng cụ thể cho trình biên dịch, không thể di chuyển. Nó không phải là một phần của C++. –

+0

Sử dụng g ++ Tôi nhận được lỗi 'hậu tố không hợp lệ' b0101 "trên hằng số nguyên' ... vì vậy rõ ràng việc sử dụng" 0b "trong nhiệm vụ KHÔNG phải là trình biên dịch chéo an toàn và không được hỗ trợ chính thức/giải pháp có thể chấp nhận được. –

+0

Thú vị, trước khi tôi nghĩ nó ở trong tiêu chuẩn và các trình biên dịch đã chọn bỏ qua nó. Không biết rằng đó là một cách khác. –

7

Với tùy chọn hai, bạn có thể sử dụng dịch trái, mà có lẽ là một chút ít "unintuitive:"

namespace DCD { 
    enum e_Feature_Flags { 
     IS_CHARMM =  1, 
     HAS_4DIMS =  (1 << 1), 
     HAS_EXTRA_BLOCK = (1 << 2), 
     NEW_FLAG =  (1 << 3), 
     NEW_FLAG_2 =  (1 << 4), 
     NEW_FLAG_3 =  (1 << 5), 
     NEW_FLAG_4 =  (1 << 6) 
    }; 
}; 
+0

Đây là cách tôi làm tất cả các thời gian trong nhúng C ... – drahnr

+0

Cách tiếp cận của Jerry bằng cách sử dụng 'bit (n)' là một chút trực quan hơn đọc mã trực quan, nhưng bạn không sử dụng macro. Gah Tôi chỉ có thể chọn một câu trả lời ... cả hai bạn đều đưa ra những gợi ý tốt. Bất kỳ thông tin chi tiết nào về lý do tại sao không bao giờ có đề xuất chuẩn hóa "0b" hoặc một số hình thức chuyển nhượng tương tự? –

4

Cũng giống như một ghi chú, Boost (như thường lệ) cung cấp một implementation của ý tưởng này.

+0

Trong khi đó thực sự hấp dẫn, Boost không chính thức là C++ và tôi cảm thấy việc kéo nó vào dự án mã của tôi sẽ giới thiệu các hạn chế nền tảng phụ thuộc mà tôi không muốn đối phó với tư cách là lập trình viên giới hạn thời gian. với C++. Hơn nữa, đồng nghiệp của tôi thậm chí còn mới làm quen với thế giới lập trình nhiều hơn tôi (tôi là một CE, vì vậy ít nhất cũng có các lớp trong Java và prog. Nguyên tắc cơ bản với kiến ​​thức cơ bản) vì vậy tôi rất tự tin rằng họ có thể duy trì mã thúc đẩy ... –

+4

Đừng sợ thư viện. Đặc biệt là Boost, đó là * rất * đa nền tảng. –

2

Tại sao không sử dụng bitfield struct?

struct preferences { 
     unsigned int likes_ice_cream : 1; 
     unsigned int plays_golf : 1; 
     unsigned int watches_tv : 1; 
     unsigned int reads_stackoverflow : 1; 
    }; 

struct preferences fred; 

fred.likes_ice_cream = 1; 
fred.plays_golf = 0; 
fred.watches_tv = 0; 
fred.reads_stackoverflow = 1; 

if (fred.likes_ice_cream == 1) 
    /* ... */ 
+0

Tôi đang đọc trong một giá trị số nguyên từ một tệp và tôi sẽ kiểm tra nó với các cờ, bằng cách sử dụng '&'. Tôi tưởng tượng giải pháp của bạn * có thể * được thích nghi với điều đó, nhưng nó có vẻ ít trực quan hơn. –

+0

Người ta cũng có thể nói rằng bạn đang đọc một bitfield 32 bit từ một tệp. –

1

Có gì sai với hex đối với trường hợp sử dụng này?

enum Flags { 
    FLAG_A = 0x00000001, 
    FLAG_B = 0x00000002, 
    FLAG_C = 0x00000004, 
    FLAG_D = 0x00000008, 
    FLAG_E = 0x00000010, 
    // ... 
}; 
2

GCC có phần mở rộng làm cho nó có khả năng phân nhị phân:

int n = 0b01010101; 

Chỉnh sửa: Tính đến C++ 14, điều này bây giờ là một phần chính thức của ngôn ngữ.

+0

Tôi khuyên bạn nên chỉnh sửa để lưu ý rằng kể từ C++ 14, đây là cú pháp chính thức, không phải là phần mở rộng. –

+0

@underscore_d cảm ơn, đã làm như vậy ngay bây giờ. – Riot