![]() |
Boost : |
From: Ruben Perez (rubenperez038_at_[hidden])
Date: 2025-05-05 10:09:57
> > 2. It sounds strange to me that policies have two functions: a. they behave
> > behavior (e.g. what to do in case of error) and b. they act as a type
> > registry. That confused me a lot, because in other libraries I got the
> > impression that policies only do a. Is there anything preventing the
> > separation of concerns here?
>
> In general, policies can have (static) state. For example,
> vectored_error_handler contains a static std::function. Each policy that uses it
> needs its own. That's the reason for all the CRTPing, and why `fork` rebinds the
> first template argument of all the templatized facets to the new policy.
>
> All the methods in the same policy count, at dispatch time, on data created by
> initialize<Policy>(). That is why methods have to be scoped in the policy. The
> same holds for class registrars: the type_ids they store at static construction
> time are not necessarily the same thing in different policies.
>
> > 3. Some of the complexity with policies (like needing a fork function) seem
> > to be stemming from the point above. What do you think? For instance,
> > couldn't facets be made regular members? Then add/fork can be implementing
> > by just inheriting from a base policy, using regular C++.
>
> Do you mean static or instance members? If it is the latter, then policies would
> become objects. Which we can pass as template parameters, but it would run into
> difficulties, because we don't have universal template parameters yet. A simple
> example: use_classes. It uses std::is_base_of to detect if the last class is a
> policy. So a policy has got to be a type (unless we change the contracts a lot).
>
> If you mean static members, we are back to the problem of separating my_policy's
> error stream from your_policy's.
>
As I think I didn't explain myself enough in my previous message, I'd
like to expand on what I meant by this. Take the current vptr_vector
facet, for example (simplified):
template<class Policy, typename Facet = void>
class vptr_vector {
static std::vector<element_type> vptrs;
public:
template<typename ForwardIterator>
static auto register_vptrs(ForwardIterator first, ForwardIterator last);
};
And the current basic_policy implementation (also simplified):
// domain<Policy> contains static members
template<class Policy, class... Facets>
struct basic_policy : abstract_policy, domain<Policy>, Facets... {
using facets = mp11::mp_list<Facets...>;
};
Could it be possible to write:
// Members are no longer static
template<class Policy, typename Facet = void>
class vptr_vector {
std::vector<element_type> vptrs;
public:
template<typename ForwardIterator>
auto register_vptrs(ForwardIterator first, ForwardIterator last);
};
// domain now contains regular members (no longer static)
template<class Policy, class... Facets>
struct basic_policy : abstract_policy, domain, Facets... {
std::tuple<Facets...> facets;
};
// A method_container is linked to a policy, and is what you use to
register methods
struct debug_method_container {
static debug_policy policy; // Instead of many static members, just this one
};
// Register classes and methods
BOOST_OPENMETHOD_CLASSES(base_node, node1, node2, debug_method_container)
If this is possible, you could simplify how you store facets, making
them regular members so you don't need add, replace and fork:
// Marker class to say "this facet it not implemented"
struct facet_not_implemented {};
struct abstract_policy {
facet_not_implemented rtti;
facet_not_implemented extern_vptr;
facet_not_implemented type_hash;
facet_not_implemented error_handler;
facet_not_implemented runtime_checks;
facet_not_implemented error_output;
facet_not_implemented trace_output;
};
struct release_policy : abstract_policy {
facet_not_implemented rtti;
fast_perfect_hash type_hash;
vptr_vector<fast_perfect_hash> extern_vptr;
vectored_error_handler<facet_not_implemented> error_handler;
};
struct debug_policy : release_policy {
runtime_checks runtime_checks;
basic_error_output<> error_output;
basic_trace_output<> trace_output;
vectored_error_handler<basic_error_output<>> error_handler;
};
This avoids CRTP and all the facets machinery. It also makes
dependencies between facets explicit, which I think it's good.
Currently, replacing type_hash seems to influence how vptr_vector
behaves, but I could only arrive at this conclusion by inspecting the
source code.
What are your thoughts on this proposal?
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk