You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于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;
}

关键改动说明

  1. 移除template<auto>:C++17不允许非类型模板参数为任意auto类型,改为显式传递数组大小N作为模板参数
  2. 替换用户定义字面量:改用make_compressed_string模板函数生成压缩对象,规避C++17对自定义字面量模板参数的限制
  3. constexpr兼容性:确保所有编译时计算逻辑符合C17规范,C17允许constexpr函数包含循环和lambda表达式
  4. 数组大小计算:通过compute_compressed_size静态constexpr函数在编译时确定压缩后数据长度,用于初始化std::array

内容的提问来源于stack exchange,提问作者Fred Helmers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 10:20:03