1

In the following code, the standalone component in the namespace S has its own definitions of Big and Small types, together with a split function to split one Big into a collection of the Smalls.

S also provides another function, work, which makes use of split, and is meant to be used by S itself and also by other dependent components, e.g. the one in the namespace D, which are assumed to provide their own definitions of Big and Small and their own definition of split, which will be identified via ADL.¹

#include <iostream>
#include <string>
#include <utility>
#include <vector>

namespace S /* standalone */ {
    struct Big{};
    struct Small{};
    std::vector<Small> split(Big b) {
        std::cout << "S" << std::endl;
        return {/* use b to make output */};
    }
    template<typename BigT>
    void work(BigT/* acutally this is a template class with `BigT` as a template paramter*/ x) {
        split(x); // correspondingly `split` is not applied on `x` but on the `BigT` which is part of it
        // a lot of complex stuff
    }
}

namespace D /* dependent on standalone */ {
    struct Big{};
    struct Small{};
    std::vector<Small> split(Big b) {
        std::cout << "D" << std::endl;
        return {/* use b to make output */};
    }
}
int main() {
    S::Big line1{};
    D::Big line2{};
    S::work(line1); // uses S::split
    S::work(line2); // uses D::split
}

Well, in reality S::split is a function object, not a function²,

namespace S {
    struct Split {
        std::vector<Small> operator()(Big) const {
            std::cout << "S" << std::endl;
            return {};
        }
    } split;
}

so ADL doesn't work.

Any suggestion on how to address these needs?

From the comments emerges that maybe Niebloids and/or tag_invoke represent an answer to my question. And I'd really like to understand more about these concepts.

For now, my understanding of Niebloids (and I'm reading this blog from Eric Niebler) is that they are function objects that (when they're in scope) prevent ADL, thus "focusing" all function calls directed to unqualified free functions with the same name as the niebloid; their operator(), however, relies on ADL to forward the call to the appropriate free function. Therefore it looks like the contrast in my example code between S::split being a function object and D::split being a free function cannot be addressed by niebloids, unless I make S::split a free function (in which case ADL would suffice in my simple scenario).


¹ Originally work was defined in both S and D, and the code above is my attempted refactoring, during which I run into the described issue.

² The reason for this is that S::split is used in several contexts in S, it has a few overloads of operator(), and most importantly, it's often passed around as an object, which is very handy.

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • Can you add an overload of `work` in `S` that accepts a function object that will be used instead of the split object from `S`? – NathanOliver Mar 01 '21 at 14:20
  • @NathanOliver, at first I'd say no, because all the refactoring I'm doing is to have `work` in one place only, from where it can be used by other components, provided they customize the behavior of `work` by providing their own `split` for their own `Big`/`Small`. However, probably you are referring to a `D::work` which would only call at `S::work` to do the job? – Enlico Mar 01 '21 at 14:25
  • No, I mean have a `work` in `S` like `template void work(BigT x, Splitter s)`and then you would have your original `work` function call this one with `work(x, S::split);`. Then all "derived" components would also call the new template and pass their own splitter to `work`. Makes a little more work at the call site but keeps everything in `S`. – NathanOliver Mar 01 '21 at 14:29
  • Are you looking for something like [that](http://coliru.stacked-crooked.com/a/24c0bd68605e5553)? – Jarod42 Mar 01 '21 at 15:16
  • 1
    You also might be interested by `tag_invoke`. – Jarod42 Mar 01 '21 at 15:18
  • @Jarod42, as regards the first comment, I'd avoid modifying the `Big` classes. It looks a lot like a convoluted workaround. As regards the second, do you refer to niebloids? – Enlico Mar 01 '21 at 15:21
  • 1
    Not familiar with `tag_invoke`, I have just read some [stuff](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf) recently, but I think yes. – Jarod42 Mar 01 '21 at 15:35

0 Answers0