std::error_category is a peculiar beast:
error_categoryobjects are passed by reference, and two such objects are equal if they have the same address. This means that applications using customerror_categorytypes should create a single object of each such type. [syserr.errcat.overview]
So, essentially they are singletons and equality between error_categories is determined by pointer-equality (see also error_category::operator== in syserr.errcat.nonvirtuals).
In our project, we use custom error_categories for reporting errors via std::system_error and ran into a nasty problem when linking several components together.
We have a custom error category defined in a static library CommonBase:
// internal singleton class
class foo_category_T : public std::error_category { /* ... */ };
// public accessor for singleton instance of foo_category_T
const error_category& foo_category();
We have a dynamic library DynLib that provides an asynchronous API for doing stuff:
typedef std::function<void(std::error_code const&)> result_callback;
void doStuffAsync(result_callback const& user_callback);
This is your ordinary asynchronous interface as you would find it for example in Boost.Asio.
DynLib statically links to CommonBase internally and uses the foo_category from CommonBase as the category for the error codes given to the callback in case of errors.
We now have an application that also statically links to CommonBase internally. This application may or may not decide to load DynLib as a plugin at runtime.
If the application does load DynLib we get into trouble, since now both components will end up with their own instance of the foo_category. Even though both instances were created by CommonBase, they are not considered equal as they refer to different objects in memory.
I was wondering if this is a problem that was considered when error_category was originally designed. So far I could see the following solutions to this problem, none of which sounds particularly appealing:
- Don't do it.
error_category(and thuserror_code) should not pass over dll boundaries. - Provide an accessor for the
DynLibinstance of the error_category on the interface of the dynamic library. This seems a little silly, as the application already knows theerror_categorytype, it simply does not have access to the pointer. - Inject the category instance created by the application when loading
DynLib. This requires changingCommonBasein a way that a category is either created upon request, or injected before the first call to thefoo_categoryfunction. This works fine for custom error categories, but what about builtin categories likestd::system_category?
I hope you can shed some light on this issue and give some advice for best practices.
Aucun commentaire:
Enregistrer un commentaire