You need to enable JavaScript to run this app. C++编译期constexpr注册外设派生类回调运行期调用方案问询
优惠活动
大模型
产品
解决方案
定价
更多

C++编译期constexpr注册外设派生类回调运行期调用方案问询

实现方案

原代码问题

  • static constexpr修饰的成员默认是只读常量,无法在构造函数中执行赋值、自增等修改操作
  • 派生类使用私有继承,导致基类指针无法指向派生类实例,注册逻辑本身不成立

可行实现(基于C++20)

C++20扩展了constexpr的能力,支持编译期修改可变常量、调用虚函数,可以满足你的需求,完整实现代码如下:

#include <stdint.h>
#include <array>
#include <iostream>

class PeripheralBase
{
public:
    // constexpr构造函数,编译期执行注册
    constexpr PeripheralBase()
    {
        if (count_cb_ < registered_cb_.size())
        {
            registered_cb_[count_cb_++] = this;
        }
    }

    virtual constexpr void Callback() = 0;

    static void RunCallback()
    {
        for (auto i = 0; i < count_cb_; i++)
        {
            registered_cb_[i]->Callback();
        }
    }

    // C++20支持inline constexpr静态成员为可变状态
    static inline constexpr std::size_t max_peripheral_num = 5;
    static inline constexpr std::array<PeripheralBase*, max_peripheral_num> registered_cb_ = []{
        std::array<PeripheralBase*, max_peripheral_num> arr{};
        return arr;
    }();
    static inline constexpr int count_cb_ = 0;
};

// 改为public继承
class Adc : public PeripheralBase
{
public:
    constexpr Adc() = default;

    void Callback() override
    {
        std::cout << "AdcCallback\n";
    }
};

class Timer : public PeripheralBase
{
public:
    constexpr Timer() = default;

    void Callback() override
    {
        std::cout << "TimerCallback\n";
    }
};

// 编译期构造实例,自动完成注册
constexpr Timer tim1;
constexpr Adc adc;

int main ()
{
    while(1)
    {
        PeripheralBase::RunCallback();
        // 测试用,防止死循环可打开下行注释
        // break;
    }
    return 0;
}

关键修改说明

  • 将注册数组和计数器修改为static inline constexpr类型,C++20标准下支持在constexpr上下文(比如constexpr构造函数)中修改这类可变常量的状态
  • 派生类改为public继承,确保基类指针可以正常指向派生类实例
  • 回调函数改为纯虚函数,强制派生类实现对应逻辑
  • 注册逻辑增加边界判断,避免数组越界

编译要求

需要使用支持C++20及以上标准的编译器编译,测试验证可在GCC 10+、Clang 13+、MSVC 2022+版本正常运行。

兼容低版本C的替代方案(无需C20)

如果无法使用C++20标准,可以借助编译器的自定义段特性实现编译期注册,以GCC为例:

  • 在基类中定义自定义段,将每个派生类实例的指针放到指定段中
  • 链接时链接器会自动把所有注册的指针放到连续的内存空间
  • 运行期遍历段的起止地址即可获取所有注册的实例指针
    这种方案不需要修改C++标准版本,适合嵌入式、内核等低版本编译器场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.13 00:33:04