c++zephyr-rtos

warning: 'offsetof' within non-standard-layout type is conditionally-supported


I'm trying to use the CONTAINER_OF macro in a C++ application for Zephyr RTOS. My struct looks like this:

struct auto_update_config {
    std::function<int()> update_value_func;
    k_work_delayable update_work;
    uint32_t num_failures;
    k_timeout_t update_period;
};

When I try to use CONTAINER_OF:

static void auto_update_work_handler(struct k_work* work) {
    struct k_work_delayable* dwork = k_work_delayable_from_work(work);
    struct auto_update_config* ctx =
        CONTAINER_OF(dwork, struct auto_update_config, update_work);
    int err = ctx->update_value_func();
    ...
}

The compiler gives a warning:

warning: 'offsetof' within non-standard-layout type 'auto_update_config' is conditionally-supported [-Winvalid-offsetof]

451 | CONTAINER_OF(dwork, struct auto_update_config, update_work);

I imagine the std::function is what's causing this warning, but I'm not sure what to do about it. I need the struct to contain a field for a function with this signature, and I can't use a C-style definition int (*update_value_func)() because the value is the result of a std::bind.

Is there a way to achieve this same effect without getting this warning (other than just disabling the warning flag)?


Solution

  • Is there a way to achieve this same effect without getting this warning (other than just disabling the warning flag)?

    You could separate your auto_update_config structure into two parts. One that contains the update_work member that you apply CONTAINER_OF to, and the other one with the rest of the members, that derives from the first one. Then you can use a static_cast to convert from the base class to the derived class to have access to update_value_func and other members.

    struct auto_update_config_base {
        k_work_delayable update_work;
    };
    
    struct auto_update_config :
        public auto_update_config_base {
        std::function<int()> update_value_func;
        uint32_t num_failures;
        k_timeout_t update_period;
    };
    
    static void auto_update_work_handler(struct k_work* work) {
        struct k_work_delayable* dwork = k_work_delayable_from_work(work);
        struct auto_update_config_base* ctx =
            CONTAINER_OF(dwork, struct auto_update_config_base, update_work);
        int err = static_cast< struct auto_update_config* >(ctx)->update_value_func();
        ...
    }
    

    The trick here is that auto_update_config_base remains a standard-layout class and hence supports offsetof. The static_cast supports non-standard-layout classes and will work correctly as long as the pointed object is always of the auto_update_config class.