But that is not the relevant pattern. What we require is the *joint* instantiation of A B and C types, and a dispatch that is specialized for this joint combination, and hence is as fast as possible.
The joint combination is straightforward: template<class A, class B, class C> void fooImpl(A a, B b, C c); void foo(std::variant<A1..> avar, std::variant<B1..> bvar, std::variant<C1..> cvar) { std::visit( [](auto a, auto b, auto c) { fooImpl(a, b, c); }, avar, bvar, cvar); } but in this case, as you pointed out, we don't get much compile time advantage - but we do still enjoy the fast dispatch.
What you are describing (independent dispatching for every type) is not very different from dynamic typing, unless the code can be divided into clear disjoint blocks as in your example, which is not the case for most algorithms in graph-tool
I will defer to your judgment on that one, though perhaps surprisingly I found this worked in the first (only) algorithm I tried applying it to: assortativity. I selected it based on how long it took to build. The results were: boost::any + typelist : 177s, 4.5GB memory std::variant for edge weights only: 37s + 1.74GB The memory reduction is very useful in that it enables parallel builds. The prototype can be found here: https://git.skewed.de/jaafar/graph-tool/compare/master...feature%2Fvariant-c... Best, Jeff On Thu, Feb 6, 2020 at 1:18 PM Tiago de Paula Peixoto <tiago@skewed.de> wrote:
Am 06.02.20 um 18:59 schrieb Jeff Trull:
std::variant. The high compile times stem simply from the fact we have to cycle through the Cartesian product of the set of types of each
I absolutely agree on that diagnosis, but I think std::variant can play a role. The key is to postpone dispatch, where possible, to refactor out one or more of the product terms.
At the moment the dispatch mechanism identifies all the concrete types first, /then/ runs the correct instantiated function. If there were more flexibility in this process, dispatching to selected type-dependent code could happen later and cover less code. Consider:
template<class A, class B, class C> void foo(A a, B b, C c) { // lots of A and B code // a single use of C // lots more A and B }
For the sake of a small amount of code involving C the entire function gets rebuilt as many times as there are C types. Now consider an approach that postponed determining C's concrete type:
template<class A, class B> void foo(A a, B b, std::variant<C1, C2, ...> c_var) { // lots of A and B std::visit([](auto const & c){ // use of C }, c_var); // lots more A and B }
If C was something based on scalar_types this would mean a factor of 6 reduction in instantiations!
But that is not the relevant pattern. What we require is the *joint* instantiation of A B and C types, and a dispatch that is specialized for this joint combination, and hence is as fast as possible.
What you are describing (independent dispatching for every type) is not very different from dynamic typing, unless the code can be divided into clear disjoint blocks as in your example, which is not the case for most algorithms in graph-tool.
Best, Tiago
-- Tiago de Paula Peixoto <tiago@skewed.de> _______________________________________________ graph-tool mailing list graph-tool@skewed.de https://lists.skewed.de/mailman/listinfo/graph-tool