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)?
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.