如何基于C++17改写C++20版编译时字符串压缩算法
C20编译时RLE压缩算法兼容C17改写需求
我开发的应用需要将包含大量重复字符的字符串以压缩/混淆形式加入二进制文件,目前采用简化改进型RLE算法。现有实现基于C20,但业务要求必须兼容C17。当前C++17的临时方案是将字符串存入YAML文件,编译时生成对应的压缩.cpp文件再链接进程序。
调研后发现:
- 某款Huffman编码方案仅支持C++20及以上
- 另一种方案压缩后数据与原数据大小一致
现在需要把下方的C20编译时字符串压缩算法改写成兼容C17的版本:
#include <cstdint> #include <algorithm> #include <iostream> #include <array> #include <span> #include <sstream> struct Array { const char* data; std::size_t size; }; constexpr std::size_t compress( const char* data, std::size_t size, char* buf ) { if ( size==0 ) return 0; std::size_t offset = 0; char lastch = *data; std::size_t counter = 0; auto push = [&]() { if ( counter <= 3 ) { for ( int j=0; j<counter; ++j ) buf[offset++] = lastch; } else { buf[offset++] = 0; buf[offset++] = lastch; buf[offset++] = counter; } counter = 0; }; lastch = data[0]; counter = 1; for ( std::size_t j=1; j<size; ++j ) { if ( (data[j]!=lastch) || (counter==255) ) { push(); lastch = data[j]; } counter++; } push(); return offset; } template< std::size_t N > struct RawContainer { char raw_data[N]; constexpr RawContainer( const char (&s)[N] ) { std::copy(s,s+N,raw_data); } constexpr operator const char* () const noexcept { return data; } constexpr auto data() const noexcept { return raw_data; } constexpr auto size() const noexcept { return N; } }; template< auto Container > struct StringCompressor { StringCompressor() noexcept { compress(Container.data(),Container.size(),compressed_data.data()); } constexpr static auto build_size() noexcept { char out[Container.size()*3]; return compress(Container.data(),Container.size(),out); } std::string str() noexcept { std::ostringstream out; out << compressed_data.size() << ": "; for ( std::size_t j=0; j<compressed_data.size(); ++j ) { out << (int)compressed_data[j] << " "; } return out.str(); } std::array<char,build_size()> compressed_data; }; template<RawContainer str> constexpr StringCompressor<str> operator ""_x() noexcept { return StringCompressor<str>(); } auto value = "aaaabbbbbbbbbbbbbbbbbbbc"_x; int main() { std::cout << value.str() << std::endl; }
注:为简洁起见未包含解压算法。
兼容C++17的改写实现
C++17不支持template<auto>以及模板参数为自定义类型的用户定义字面量,核心改动围绕这些限制展开,同时保留原有的编译时压缩逻辑:
#include <cstdint> #include <algorithm> #include <iostream> #include <array> #include <sstream> struct Array { const char* data; std::size_t size; }; // 压缩逻辑保持不变,C++17支持该constexpr函数实现 constexpr std::size_t compress(const char* data, std::size_t size, char* buf) { if (size == 0) return 0; std::size_t offset = 0; char lastch = data[0]; std::size_t counter = 1; auto push = [&]() { if (counter <= 3) { for (std::size_t j = 0; j < counter; ++j) { buf[offset++] = lastch; } } else { buf[offset++] = 0; buf[offset++] = lastch; buf[offset++] = static_cast<char>(counter); } counter = 0; }; for (std::size_t j = 1; j < size; ++j) { if ((data[j] != lastch) || (counter == 255)) { push(); lastch = data[j]; } counter++; } push(); return offset; } // RawContainer结构保持不变,C++17支持constexpr构造函数 template<std::size_t N> struct RawContainer { char raw_data[N]; constexpr RawContainer(const char (&s)[N]) { std::copy(s, s + N, raw_data); } constexpr const char* data() const noexcept { return raw_data; } constexpr std::size_t size() const noexcept { return N; } }; // 替换template<auto>为显式传递数组大小模板参数 template<std::size_t N> struct StringCompressor { constexpr StringCompressor(const RawContainer<N>& container) noexcept { compress(container.data(), container.size(), compressed_data.data()); } // 编译时计算压缩后数据大小,C++17支持该constexpr静态函数 static constexpr std::size_t compute_compressed_size(const char (&s)[N]) { char out[N * 3]{}; return compress(s, N, out); } std::string str() noexcept { std::ostringstream out; out << compressed_data.size() << ": "; for (std::size_t j = 0; j < compressed_data.size(); ++j) { out << static_cast<int>(compressed_data[j]) << " "; } return out.str(); } // 用编译时计算的大小初始化数组 std::array<char, compute_compressed_size(RawContainer<N>::raw_data)> compressed_data; }; // 替代原用户定义字面量,C++17不支持模板参数为自定义类型的用户字面量 template<std::size_t N> constexpr auto make_compressed_string(const char (&s)[N]) { return StringCompressor<N>(RawContainer<N>(s)); } // 使用方式调整为调用辅助函数 constexpr auto value = make_compressed_string("aaaabbbbbbbbbbbbbbbbbbbc"); int main() { std::cout << value.str() << std::endl; }
关键改动说明
- 移除
template<auto>:C++17不允许非类型模板参数为任意auto类型,改为显式传递数组大小N作为模板参数 - 替换用户定义字面量:改用
make_compressed_string模板函数生成压缩对象,规避C++17对自定义字面量模板参数的限制 - constexpr兼容性:确保所有编译时计算逻辑符合C17规范,C17允许constexpr函数包含循环和lambda表达式
- 数组大小计算:通过
compute_compressed_size静态constexpr函数在编译时确定压缩后数据长度,用于初始化std::array
内容的提问来源于stack exchange,提问作者Fred Helmers
相关产品推荐
相关产品推荐

