Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Member is private" although I don't access it from outside, when using trailing return type

How can I fix the following problem?

I'm writing some functional library which defines the following functions which are relevant for this question:

  • call(f,arg): Calls a function with an argument. Just a wrapper I need for some situations.
  • comp(f1,f2): Returns a composition of two functions. Returns a helper functor representing the composition of the two functions.

The implementation looks like the following (simplified versions which still demonstrate the problem):

// Call f with one argument
template <class Fn, class Arg>
auto call(const Fn &f, const Arg & arg) -> decltype(f(arg)) {
    return f(arg);
}

// Helper functor for the function below
template<class Fn1, class Fn2>
class CompFn {
    Fn1 a;
    Fn2 b;

public:
    CompFn(const Fn1 &f1, const Fn2 &f2) : a(f1), b(f2) {}

    template<class Arg> inline
    auto operator()(const Arg & arg) const -> decltype(call(b, call(a, arg))) {
        return call(b, call(a, arg));
    }
};

/** Composition of f1 and f2 (f2 after f1). */
template<class Fn1, class Fn2>
CompFn<Fn1,Fn2> comp(const Fn1 &f1, const Fn2 &f2) {
    return CompFn<Fn1,Fn2>(f1, f2);
}

The following code is used as a simple test:

// Example: Take the length of the string and compare it against zero.
std::function<int(std::string)> stringLength = [](std::string s) { return s.size(); };
std::function<bool(int)> greaterZero = [](int x) { return x > 0; };
auto stringNotEmpty = comp(stringLength, greaterZero);

std::string testInput1 = "foo";
std::string testInput2 = "";

Until here, everything works fine. Calling comp itself doesn't seem to be a problem. Calling the resulting function directly is also OK. But calling the composition via call introduces an infinity number of compilation errors (yaaay, new record!):

assert(call(stringNotEmpty,testInput1) == true);    // line 44
assert(call(stringNotEmpty,testInput2) == false);

The compilation output (gcc 4.7, full output see ideone links below):

prog.cpp:16:9: error: ‘std::function<bool(int)> CompFn<std::function<int(std::basic_string<char>)>, std::function<bool(int)> >::b’ is private
prog.cpp:44:5: error: within this context
prog.cpp:15:9: error: ‘std::function<int(std::basic_string<char>)> CompFn<std::function<int(std::basic_string<char>)>, std::function<bool(int)> >::a’ is private
prog.cpp:44:5: error: within this context
prog.cpp:22:10: error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum) substituting ‘template<class Fn, class Arg> decltype (f(arg)) call(const Fn&, const Arg&) [with Fn = std::function<int(std::basic_string<char>)>; Arg = std::basic_string<char>]’
prog.cpp:22:10:   required by substitution of ‘template<class Arg> decltype (call(((const CompFn*)this)->CompFn<Fn1, Fn2>::b, call(((const CompFn*)this)->CompFn<Fn1, Fn2>::a, arg))) CompFn::operator()(const Arg&) const [with Arg = Arg; Fn1 = std::function<int(std::basic_string<char>)>; Fn2 = std::function<bool(int)>] [with Arg = std::basic_string<char>]’
prog.cpp:8:6:   required by substitution of ‘template<class Fn, class Arg> decltype (f(arg)) call(const Fn&, const Arg&) [with Fn = std::function<int(std::basic_string<char>)>; Arg = std::basic_string<char>]’
prog.cpp:22:10:   required by substitution of ‘template<class Arg> decltype (call(((const CompFn*)this)->CompFn<Fn1, Fn2>::b, call(((const CompFn*)this)->CompFn<Fn1, Fn2>::a, arg))) CompFn::operator()(const Arg&) const [with Arg = Arg; Fn1 = std::function<int(std::basic_string<char>)>; Fn2 = std::function<bool(int)>] [with Arg = std::basic_string<char>]’
prog.cpp:8:6:   required by substitution of ‘template<class Fn, class Arg> decltype (f(arg)) call(const Fn&, const Arg&) [with Fn = std::function<int(std::basic_string<char>)>; Arg = std::basic_string<char>]’
prog.cpp:22:10:   required by substitution of ‘template<class Arg> decltype (call(((const CompFn*)this)->CompFn<Fn1, Fn2>::b, call(((const CompFn*)this)->CompFn<Fn1, Fn2>::a, arg))) CompFn::operator()(const Arg&) const [with Arg = Arg; Fn1 = std::function<int(std::basic_string<char>)>; Fn2 = std::function<bool(int)>] [with Arg = std::basic_string<char>]’
prog.cpp:8:6:   [ skipping 890 instantiation contexts ]
[ ...continues endlessly... ]

When converting the composition to a std::function, it's also perfectly fine. But this will not allow to use polymorphic functors with my comp function, at least I don't see an option.

One "fix" is to not use trailing return type with decltype for the Comp::operator(), but fixing the return type to bool (specialized for this single test scenario).

All four mentioned test cases summarized:

  • Test1 -- Call the composition directly --> OK
  • Test2 -- Call the composition using call --> Error
  • Test3 -- Cast the composition to std::function, then call using call --> OK
  • Test4 -- Call the composition using call. Fixed return type of Comp::operator() to bool --> OK

My goal is to make call a "seemless" wrapper to call any type of function: Functors, function pointers, member function pointers, member variable pointers, etc..., and also a composition using comp. I have a bunch of overloads for them but I don't want to introduce an overload for Comp<Fn1,Fn2> since Fn1 or Fn2 can again be any type of function, it seems to be a "recursive problem".

like image 405
leemes Avatar asked May 01 '13 22:05

leemes


2 Answers

Clang compiles your failing test case just fine, and I can't see any error with it, so I think this is a GCC bug. Please file a bug-report with a minimal repro (no includes) if you can.

Note: For call, there's already something like it in the standard - INVOKE, which is not a macro, but a concept, so to speak. It's used by std::bind, std::function and other things, one of which is std::reference_wrapper. This means that you can do std::ref(fun)(args...) to achieve the same as call.

like image 92
Xeo Avatar answered Nov 17 '22 11:11

Xeo


Try substituting an expression of Fn1 for a, Fn2 for b to avoid mentioning private members. I tried this in VC++, but got a different error:

template<class Arg> inline
auto operator()(const Arg & arg) const -> decltype(call(Fn1(), call(Fn2(), arg))) {
    return call(b, call(a, arg));
}
like image 36
Scott Jones Avatar answered Nov 17 '22 11:11

Scott Jones