$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
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?