我有一组选项,一些是正交的(可以任意组合组合),一些是独占的(只允许一组中的一个),并且需要选择一组enum
值以便它们可以与逐位or
组合并使用逐位and
提取。我希望or
- 无效组合是可检测的。
是否有工具用于生成此类enums
?
为了清晰起见而编辑
我正在寻找能够利用某些标志组合无效以减少使用的位数的事实。我能够检测错误的要求是软的。我不需要知道如果事情被搞砸了会使用什么。
我正在使用C#,但任何解决方案都应该有用。
示例模式是:
0011 00
0101 00
1001 00
0110 00
1010 00
1100 00
0000 01
0000 10
获得6个独占标志和一个2到6位的正交对
快速测试表明,5位给出9个值,6位给出20,......
答案 0 :(得分:5)
我知道这样做的最好的通用方法不是一个常规的工具:定义像这样的位标志列表:
FLAG_1 0x00000001
FLAG_2 0x00000002
FLAG_3 0x00000004
FLAG_4 0x00000008
FLAG_5 0x00000010
FLAG_6 0x00000020
这很容易使用,因为数字继续向左移动1,2,4,8模式。
编辑:回应评论。好吧,如果你真的想要一个bitflags与独占枚举的组合,你基本上要做的是将位列表的一部分分割出来作为数字空间。所以你可以取两位0x1和0x2,现在你可以使用这两位代表0-3。类似的东西:
OPT_1_VAL_1 0x00000000
OPT_1_VAL_2 0x00000001
OPT_1_VAL_3 0x00000002
OPT_1_VAL_4 0x00000003
FLAG_1 0x00000004
FLAG_2 0x00000008
FLAG_3 0x00000010
FLAG_4 0x00000020
您使用的屏蔽逻辑必须更复杂。要查找标记,您可以执行if(settings& FLAG_1),但是如果((settings& OPT_1_VAL_3)== OPT_1_VAL_3),您可以执行选项空格。
答案 1 :(得分:3)
我不知道有什么工具,但这里有一个让特殊位枚举更容易制作的技巧:
public enum Critters
{
Amorphous = 0,
Sloth = 1 << 0,
Armadillo = 1 << 1,
Weasel = 1 << 2,
Crab = 1 << 3,
Partridge = 1 << 4,
Parakeet = 1 << 5,
Rhino = 1 << 6
};
答案 2 :(得分:3)
要表示“{strong>独占”一组n
选项(即必须选择一个),我们至少需要ceil(log2(n))
位。例如,选项k
可以用基数k
中的2
来表示。
要表示“正交”一组n
个选项(即可以选择大小0, 1, ..., n
的任意组合),我们至少需要n
位。例如,选项k0, k1, k2
可以用二进制数表示,除了位0, 1, 2
之外,其位为零。
因此,为了同时表示多个选项集,我们将每个选项集所需的位数(取决于它是“独占”还是“正交”)相加,以获得所需的总位数。
简而言之,要选择枚举值
k
使用k << r
k0, k1, ..., k{n-1}
使用0x1 << r, 0x1 << (r+1), ..., 0x1 << (r+n-1)
其中offset r
是前面选项集使用的位数。
如何自动化此构造的示例,在Java中:
/**
* Construct a set of enum values, for the given sizes and types
* (exclusive vs orthogonal) of options sets.
*
* @param optionSetSizes
* number of elements in each option set
* @param isOptionSetExclusive
* true if corresponding option set is exclusive, false if
* orthogonal
* @returns
* array of m elements representing the enum values, where
* m is the sum of option set sizes. The enum values are
* given in the order of the option sets in optionSetSizes
* and isOptionSetExclusive.
*/
int[] constructEnumValues(
int[] optionSetSizes,
boolean[] isOptionSetExclusive)
{
assert optionSetSizes.length == isOptionSetExclusive.length;
// determine length of the return value
int m = 0;
for (int i = 0; i < optionSetSizes.length; i++) m += optionSetSizes[i];
int[] vals = new int[m];
int r = 0; // number of bits used by the preceding options sets
int c = 0; // counter for enum values used
for (int i = 0; i < optionSetSizes.length; i++)
{
// size of this option set
int n = optionSetSizes[i];
// is this option set exclusive?
boolean exclusive = isOptionSetExclusive[i];
for (int k = 0; k < n; k++)
{
vals[c] = (exclusive) ? (k << r) : (0x1 << (r + k));
c++;
}
r += (exclusive) ? (int) Math.ceil(Math.log(n)/Math.log(2)) : n;
}
return vals;
}
答案 3 :(得分:2)
答案 4 :(得分:2)
使用2的幂,使每个标志对应一个位位置。
答案 5 :(得分:1)
您可以使用标准枚举(在C#中)来实现此目的。要完成此操作,您需要设置FlagsAttribute,然后专门对值进行编号。代码看起来像这样:
[Flags]
public enum AvailableColours {
black = 1,
red = 2,
green = 4,
blue = 8,
white = 16,
}
然后,标准的按位运算符将按预期工作。
[编辑] 嗯,好的,你想生成可能的组合,对吧?您的要求非常具体,如果有任何工具接近您想要的,我会非常惊讶。我想你将不得不自己动手。我假设你想要这些作为字符串,对吗?以下是一些实用程序代码,至少可以帮助您入门:
public const int BITS_IN_BYTE = 8;
public const int BYTES_IN_INT = sizeof(int);
public const int BITS_IN_INT = BYTES_IN_INT * BITS_IN_BYTE;
/// <summary>
/// Display the bits in an integer
/// </summary>
/// <param name="intToDisplay">The integer to display</param>
/// <returns>A string representation of the bits</returns>
public string IntToBitString(int intToDisplay) {
StringBuilder sb = new StringBuilder();
AppendBitString(intToDisplay, sb);
return sb.ToString();
}
/// <summary>
/// Displays the bits in an integer array
/// </summary>
/// <param name="intsToDisplay">Arrau to display</param>
/// <returns>String representation of the bits</returns>
public string IntArrayToBitString(int[] intsToDisplay) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < intsToDisplay.Length -1; i++) {
AppendBitString(intsToDisplay[i], sb);
sb.Append(' ');
}
if (intsToDisplay.Length - 1 > 0)
AppendBitString(intsToDisplay[intsToDisplay.Length - 1], sb);
return sb.ToString();
}
private void AppendBitString(int intToAppend, StringBuilder sb) {
for (int j = BITS_IN_INT - 1; j >= 0; j--) {
sb.Append((intToAppend >> j) & 1);
if (j % 4 == 0 && j > 1)
sb.Append(' ');
}
}
/// <summary>
/// Creates an integer from a bit string. This method can be used
/// to explicitly set bits in an integer during testing.
/// </summary>
/// <example>
/// int i = bitUtil.IntFromBitString("0000 0000 0000 0100");
/// </example>
/// <param name="bitString">String representing the individual bits</param>
/// <returns></returns>
public int IntFromBitString(String bitString) {
int returnInt = 0;
int currentBitPos = bitString.Length;
for (int i = bitString.Length - 1; i >= 0; i--) {
char c = bitString[i];
if (Char.IsWhiteSpace(c)) continue;
if (c == '1') {
returnInt |= 1 << (bitString.Length - currentBitPos);
}
currentBitPos--;
}
return returnInt;
}
/// <summary>
/// Tests the status of an individual bit in and integer. It is 0 based starting from the most
/// significant bit.
/// </summary>
/// <param name="bits">The integer to test</param>
/// <param name="pos">The position we're interested in</param>
/// <returns>True if the bit is set, false otherwise</returns>
public bool IsBitOn(int bits, int pos) {
int shiftAmnt = (BITS_IN_INT - 1) - pos;
return ((bits >> shiftAmnt) & 1) == 1;
}
/// <summary>
/// Calculates the number of integers (as in an array of ints) required to
/// store a given number of bits
/// </summary>
/// <param name="bitsNeeded">The total count of required bits</param>
/// <returns>The number of integers required to represent a given bit count</returns>
public int RequiredSizeOfIntArray(int bitsNeeded) {
return (bitsNeeded / BITS_IN_INT) + (((bitsNeeded % BITS_IN_INT) == 0) ? 0 : 1);
}
/// <summary>
/// Calculates which array element would hold the individual bit for a given bit position
/// </summary>
/// <param name="bitPos">The position of the interesting bit</param>
/// <returns>An index into an array of integers</returns>
public int ArrayPositionForBit(int bitPos) {
return bitPos / BITS_IN_INT;
}
/// <summary>
/// Sets an individual bit to a given value
/// </summary>
/// <param name="bits">The integer containing the bits</param>
/// <param name="pos">The position in the integer to set</param>
/// <param name="isSet">True for on, False for off</param>
public void SetBit(ref int bits, int pos, bool isSet) {
int posToSet = (BITS_IN_INT - 1) - pos;
if (isSet)
bits |= 1 << posToSet;
else
bits &= ~(1 << posToSet);
}
/// <summary>
/// Converts an array of integers into a comma seperated list
/// of hexidecimal values.
/// </summary>
/// <param name="bits">The array of integers</param>
/// <returns>String format</returns>
public String IntArrayToHexString(int[] bits) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bits.Length - 1; i++) {
sb.Append(bits[i].ToString("X"));
sb.Append(',');
}
if (bits.Length > 0) {
sb.Append(bits[bits.Length - 1].ToString("X"));
}
return sb.ToString();
}
/// <summary>
/// Parses a comma seperated list of hexidecimal values and
/// returns an array of integers for those values
/// </summary>
/// <param name="hexString">Comma seperated hex values</param>
/// <returns>integer array</returns>
public int[] HexStringToIntArray(String hexString) {
string[] hexVals = hexString.Split(new char[] {','});
int[] retInts = new int[hexVals.Length];
for (int i = 0; i < hexVals.Length; i++) {
retInts[i] = Int32.Parse(hexVals[i], System.Globalization.NumberStyles.HexNumber);
}
return retInts;
}
答案 6 :(得分:1)
您确定需要使用位字段吗?
根据我的经验,具有一组布尔数据成员的类几乎总是最佳选择。
我曾经听说过使用位字段而不是更大的布尔值(通常是一个字节)的唯一论据是它应该更快。与所有优化一样,在不测量性能的情况下进行此操作是一个坏主意。
一旦将其封装在一个类中,如果您决定更改表示以进行优化,则可以在不需要执行大量其他代码的情况下执行此操作。