在编译时初始化非常大的 C++ std::bitset
Initializing very large C++ std::bitset at compile time
我想存储一个 216 位的静态常量位集,其中 1 和 0 的特定序列永远不会改变。
我考虑过使用 this post 提出的初始化字符串:
std::bitset<1<<16> myBitset("101100101000110 ... "); // the ellipsis are replaced by the actual 65536-character sequence
但是编译器 (VS2013) 给我 "string too long" 错误。
更新
我尝试按照上面链接的 post 中的建议将字符串拆分成更小的块,如下所示:
std::bitset<1<<16> myBitset("100101 ..."
"011001 ..."
...
);
但我收到错误 C1091:编译器限制:字符串长度超过 65535 字节。我的字符串是 65536 字节(技术上是 65537,带有 EOS 字符)。
我还有哪些其他选择?
更新
感谢 luk32,这是我最终得到的漂亮代码:
const std::bitset<1<<16> bs = (std::bitset<1<<16>("101011...")
<< 7* (1<<13)) | (std::bitset<1<<16>("110011...")
<< 6* (1<<13)) | (std::bitset<1<<16>("101111...")
<< 5* (1<<13)) | (std::bitset<1<<16>("110110...")
<< 4* (1<<13)) | (std::bitset<1<<16>("011011...")
<< 3* (1<<13)) | (std::bitset<1<<16>("111011...")
<< 2* (1<<13)) | (std::bitset<1<<16>("111001...")
<< 1* (1<<13)) | std::bitset<1<<16>("1100111...");
你并没有真正拆分文字。无论如何,它都会被连接起来进行编译。您受到编译器的限制。我认为没有办法在 MSVC 中增加此限制。
您可以将其拆分为两个文字,初始化两个位集,移动第一个部分并 OR
与另一个一起移动。
类似于:
#include <iostream>
#include <string>
#include <bitset>
using namespace std;
int main()
{
std::bitset<8> dest("0110");
std::bitset<8> lowBits("1001");
dest <<= dest.size()/2;
dest |= lowBits;
std::cout << dest << '\n';
}
如果您查看 clang compiler output at -02
,它会优化为加载 105
,即 01101001
。
我的测试表明,如果您将 8
换成 1<<16
,它会使用 SSE,因此应该是非常安全的选择。它没有像 8
或 16
那样丢弃文字,因此可能会有一些运行时开销,但我不确定您是否可以做得更好。
编辑:
我又做了一些测试,这里是my playground:
#include <iostream>
#include <string>
#include <bitset>
using namespace std;
int main()
{
//static const std::bitset<16> set1( "01100110011001100110011001100110");
static const std::bitset<16> set2(0b01100110011001100110011001100110);
static const std::bitset<16> high(0b01100110);
static const std::bitset<16> low (0b01100110);
static const std::bitset<16> set3 = (high << 8) | low;
std::cout << (set3 == set2) << '\n';
}
我无法在除 clang 之外的任何编译器上对 const char*
构造函数进行编译时优化,并且最多可处理 14 个字符。 似乎有一些承诺如果你从 unsigned long long
初始化一堆 bitset
并将它们组合在一起:
static const std::bitset<128> high(0b0110011001100110011001100110011001100110011001100110011001100110);
static const std::bitset<128> low (0b1001100110011001100110011001100110011001100110011001100110011001);
static const std::bitset<128> set3 = (high << high.size()/2) | low;
std::cout << set3 << '\n';
这使得编译器坚持使用二进制数据存储。如果可以使用带有 constexpr
的更新一点的编译器,我认为可以将其声明为由 ull
构造的 bitset
的数组,并通过 [=24= 将它们连接起来] 函数并绑定到 constexpr const
变量,这应该确保可能的最佳优化。编译器仍然可能不利于你,但没有理由。也许即使没有 constexpr
它也会生成非常好的代码。
您可以考虑完全跳过编译,并且简单地:
- Assemble 将数据放入目标文件(段
.rodata
),导出它的符号及其大小。
- 在
.h
文件中将这些符号声明为 extern const
。
- 使用这些符号和link你的程序到这个目标文件。
我手边没有 MASM32,无法写出实际有效的完整答案,但我经常将这种技术与 GAS 和 LD 一起使用,它可以解决很多问题。 (按需加载、单独数据文件的安全描述符、极快的编译时间...)
请注意,简而言之,这是 VS 资源编译器所做的...因此您可以将数据作为资源包含在内并获取指向它的指针。
不可能 有一个 static std::bitset
这样的,因为:
- 没有
constexpr
支持 constructor receiving const char*
- VS 2013 非常老 doesn't even support
constexpr
。它只有部分 C++11 支持
如果允许在运行时进行构造,则只需将字符串文字拆分为多个小于 2048 个字符的较小字符串,以防总长度小于 65536:
ANSI compatibility requires a compiler to accept up to 509 characters in a string literal after concatenation. The maximum length of a string literal allowed in Microsoft C is approximately 2,048 bytes. However, if the string literal consists of parts enclosed in double quotation marks, the preprocessor concatenates the parts into a single string, and for each line concatenated, it adds an extra byte to the total number of bytes.
[...]
While an individual quoted string cannot be longer than 2048 bytes, a string literal of roughly 65535 bytes can be constructed by concatenating strings.
https://docs.microsoft.com/en-us/cpp/c-language/maximum-string-length?view=msvc-160
如上所述,较长的字符串必须手动连接。这里
const int LENGTH = 1 << 16;
std::bitset<LENGTH> myBitset(
"100101 ..." // 2ᴺ bits
"011001 ..." // 2ᴺ bits
...
"001011 ...", // must be one shorter than the previous lines: 2ᴺ⁻¹ bits
LENGTH - 1 // size
);
myBitset[LENGTH - 1] = 1; // set the final bit
或者只使用数组而不是字符串文字:
static const char BITSET[LENGTH] = {
'1', '0', '0', '1',...
...
'0', '1', '0', '0'
};
std::bitset<LENGTH> myBitset(BITSET, sizeof(BITSET));
我想存储一个 216 位的静态常量位集,其中 1 和 0 的特定序列永远不会改变。
我考虑过使用 this post 提出的初始化字符串:
std::bitset<1<<16> myBitset("101100101000110 ... "); // the ellipsis are replaced by the actual 65536-character sequence
但是编译器 (VS2013) 给我 "string too long" 错误。
更新
我尝试按照上面链接的 post 中的建议将字符串拆分成更小的块,如下所示:
std::bitset<1<<16> myBitset("100101 ..."
"011001 ..."
...
);
但我收到错误 C1091:编译器限制:字符串长度超过 65535 字节。我的字符串是 65536 字节(技术上是 65537,带有 EOS 字符)。
我还有哪些其他选择?
更新
感谢 luk32,这是我最终得到的漂亮代码:
const std::bitset<1<<16> bs = (std::bitset<1<<16>("101011...")
<< 7* (1<<13)) | (std::bitset<1<<16>("110011...")
<< 6* (1<<13)) | (std::bitset<1<<16>("101111...")
<< 5* (1<<13)) | (std::bitset<1<<16>("110110...")
<< 4* (1<<13)) | (std::bitset<1<<16>("011011...")
<< 3* (1<<13)) | (std::bitset<1<<16>("111011...")
<< 2* (1<<13)) | (std::bitset<1<<16>("111001...")
<< 1* (1<<13)) | std::bitset<1<<16>("1100111...");
你并没有真正拆分文字。无论如何,它都会被连接起来进行编译。您受到编译器的限制。我认为没有办法在 MSVC 中增加此限制。
您可以将其拆分为两个文字,初始化两个位集,移动第一个部分并 OR
与另一个一起移动。
类似于:
#include <iostream>
#include <string>
#include <bitset>
using namespace std;
int main()
{
std::bitset<8> dest("0110");
std::bitset<8> lowBits("1001");
dest <<= dest.size()/2;
dest |= lowBits;
std::cout << dest << '\n';
}
如果您查看 clang compiler output at -02
,它会优化为加载 105
,即 01101001
。
我的测试表明,如果您将 8
换成 1<<16
,它会使用 SSE,因此应该是非常安全的选择。它没有像 8
或 16
那样丢弃文字,因此可能会有一些运行时开销,但我不确定您是否可以做得更好。
编辑:
我又做了一些测试,这里是my playground:
#include <iostream>
#include <string>
#include <bitset>
using namespace std;
int main()
{
//static const std::bitset<16> set1( "01100110011001100110011001100110");
static const std::bitset<16> set2(0b01100110011001100110011001100110);
static const std::bitset<16> high(0b01100110);
static const std::bitset<16> low (0b01100110);
static const std::bitset<16> set3 = (high << 8) | low;
std::cout << (set3 == set2) << '\n';
}
我无法在除 clang 之外的任何编译器上对 const char*
构造函数进行编译时优化,并且最多可处理 14 个字符。 似乎有一些承诺如果你从 unsigned long long
初始化一堆 bitset
并将它们组合在一起:
static const std::bitset<128> high(0b0110011001100110011001100110011001100110011001100110011001100110);
static const std::bitset<128> low (0b1001100110011001100110011001100110011001100110011001100110011001);
static const std::bitset<128> set3 = (high << high.size()/2) | low;
std::cout << set3 << '\n';
这使得编译器坚持使用二进制数据存储。如果可以使用带有 constexpr
的更新一点的编译器,我认为可以将其声明为由 ull
构造的 bitset
的数组,并通过 [=24= 将它们连接起来] 函数并绑定到 constexpr const
变量,这应该确保可能的最佳优化。编译器仍然可能不利于你,但没有理由。也许即使没有 constexpr
它也会生成非常好的代码。
您可以考虑完全跳过编译,并且简单地:
- Assemble 将数据放入目标文件(段
.rodata
),导出它的符号及其大小。 - 在
.h
文件中将这些符号声明为extern const
。 - 使用这些符号和link你的程序到这个目标文件。
我手边没有 MASM32,无法写出实际有效的完整答案,但我经常将这种技术与 GAS 和 LD 一起使用,它可以解决很多问题。 (按需加载、单独数据文件的安全描述符、极快的编译时间...)
请注意,简而言之,这是 VS 资源编译器所做的...因此您可以将数据作为资源包含在内并获取指向它的指针。
不可能 有一个 static std::bitset
这样的,因为:
- 没有
constexpr
支持 constructor receivingconst char*
- VS 2013 非常老 doesn't even support
constexpr
。它只有部分 C++11 支持
如果允许在运行时进行构造,则只需将字符串文字拆分为多个小于 2048 个字符的较小字符串,以防总长度小于 65536:
ANSI compatibility requires a compiler to accept up to 509 characters in a string literal after concatenation. The maximum length of a string literal allowed in Microsoft C is approximately 2,048 bytes. However, if the string literal consists of parts enclosed in double quotation marks, the preprocessor concatenates the parts into a single string, and for each line concatenated, it adds an extra byte to the total number of bytes.
[...]
While an individual quoted string cannot be longer than 2048 bytes, a string literal of roughly 65535 bytes can be constructed by concatenating strings.
https://docs.microsoft.com/en-us/cpp/c-language/maximum-string-length?view=msvc-160
如上所述,较长的字符串必须手动连接。这里
const int LENGTH = 1 << 16;
std::bitset<LENGTH> myBitset(
"100101 ..." // 2ᴺ bits
"011001 ..." // 2ᴺ bits
...
"001011 ...", // must be one shorter than the previous lines: 2ᴺ⁻¹ bits
LENGTH - 1 // size
);
myBitset[LENGTH - 1] = 1; // set the final bit
或者只使用数组而不是字符串文字:
static const char BITSET[LENGTH] = {
'1', '0', '0', '1',...
...
'0', '1', '0', '0'
};
std::bitset<LENGTH> myBitset(BITSET, sizeof(BITSET));