9

C++ has ADL (Argument Dependent Lookup) by which, as its name describes, the context (namespace) of a function can be implied from the context (namespace) of (any of) the argument(s).

fun(a); // if the type of a is in namespace ns deduce ns::f if available

My question is if the reverse is also possible by some technique? By reverse I mean if the context (namespace) can be deduced from the context of the called function. Some sort of "Function Dependent Lookup" (FDL). Fake code:

ns::fun(a); // deduce ns::a if available

I can't figure out a way of doing that. This limitation is particularly annoying for enums used to encode functions options. I would like to know if there is a technique to simulate this feature (C++11 would be ok too). Fake code:

ns::fun(Saturday, Tuesday); // Saturday/Tuesday are enum values in namespace ns;

Especially if there is a workaround for enums.

This code illustrates the issue:

namespace longname{
    class A{};
    void fun(A const& a){}
    A global_a;

    enum Days { Saturday,Sunday,Tuesday,Wednesday,Thursday,Friday};
    void gun(Days d1, Days d2){}    
}

int main(){
    longname::A a;
    fun(a); // cool, longname::fun(a) not necessary, fun is deduced from context

    longname::fun(global_a); // error, not cool, global_a context not deduced, 
    // must use then longname::fun(longname::global_a)
    longname::gun(Saturday, Tuesday); // error, particularly not cool, the Saturday is not deduced from context 
    // must use then longname::gun(longname::Saturday, longname::Tuesday)
    // or at best gun(longname::Saturday, longname::Tuesday)
}

EDIT: @jrok suggested a workaround based on defining nested namespace. For the enum case, I get this code. Which still has some noise (there is really no "dependent" lookup at all) but it is an improvement.

namespace longname{
    namespace days{
        enum _ { Saturday,Sunday,Tuesday,Wednesday,Thursday,Friday};
    }
    void gun(days::_ d1, days::_ d2){}  
}

int main(){
    using namespace longname::days; // some noise still here
    longname::gun(Saturday, Tuesday);
}

I am not using enum class because then Saturday, Sunday, etc cannot be brough directly in scope (in fact using longname::days::_ would give me a compile error)

alfC
  • 14,261
  • 4
  • 67
  • 118
  • ok, after submitting my question: I got a related question on the right panel http://stackoverflow.com/questions/14163667/why-does-c11-not-support-name-lookup-like-this?rq=1. Arguably, yhe difference here is that I am not questioning the language but I am looking for a workaround technique. – alfC Dec 15 '13 at 22:10
  • 2
    Workaround: Put the enum in a nested namespace and say `using namespace longname::nested;` in `main`. – jrok Dec 15 '13 at 22:16
  • @jrok, cool, that gets one closer to a solution (I added your suggestion to the question). – alfC Dec 15 '13 at 22:42
  • `namespace Longname { enum class Days { Saturday, Monday, Odinsday }; } using Longname::Days; Longname::func( Days::Saturday, Days::Odinsday );`? – Yakk - Adam Nevraumont Dec 16 '13 at 03:33
  • @Yakk, thanks I put something similar in my edit, `enum class` makes it harder to remove noise, compared to `enum` (but necessary for other reasons). – alfC Dec 16 '13 at 04:12
  • This stack overflow on conditional templates may help http://stackoverflow.com/questions/6251889/type-condition-in-template – Richard Chambers Dec 16 '13 at 16:53
  • @RichardChambers, I don't see how conditional templates help here. Are you talking about passing enum-like parameters as template arguments? Still the namespace of the arguments is not deduced, is it? – alfC Dec 16 '13 at 17:15
  • I was thinking in terms of template metaprogramming. Not something I have done much of however here is an interesting article with some of the basics http://www.codeproject.com/Articles/3743/A-gentle-introduction-to-Template-Metaprogramming – Richard Chambers Dec 16 '13 at 18:39

1 Answers1

3

Yes and no. Mostly no.

The bad news is if an enum is outside the current scope, such as Tuesday, etc then it can't be passed to a function, even if that function was declared in a namespace where the enum was visible. This is because argument lookup occurs first when you write a function call and the arguments can not be passed to gun and then have name lookup occur. Nothing can change this - however there is good news too.

Firstly you seem to need behaviour that maps ns::foo(arg1, arg2) -> {using namespace ns; ns::foo(arg1, arg2);}. Function calls and templates can't change this but macros kind of can and I included and example.

Also I gave a basic example of argument dependent lookup. You can see that the out-of-scope functions GetMonday and GetTuesday (which return your out-of-scope enum) can be found using this mechanism simply because you included one type from that namespace. RegisterNamespace::val adds the hidden namespace to the scope when the compiler is trying to find GetMonday, and GetMonday returns a Days which allows the compiler to find foo.

Really you want the compiler to alter the scope by adding additional namespaces when it encounters a function from another namespace. However the compiler has already determined the types of the arguments by then, and actually needs them to work out other possible alternatives to the function.

#include <iostream>

namespace hidden {

enum RegisterNamespace { val };

enum Days {
    Monday,
    Tuesday
};

void foo(Days a , Days b){std::cout << "Called foo\n";}

Days GetMonday(RegisterNamespace a = val){return Days::Monday;}
Days GetTuesday(RegisterNamespace b = val){return Days::Tuesday;}

}

using namespace std;

#define UseNamespace(ns, x) do {using namespace ns; x;} while (0)

int main()
{
    //with a macro
    UseNamespace(hidden,hidden::foo(Monday, Tuesday));

    {
    //foo is found by argument dependent lookup
    using hidden::Days;
    foo(Days::Monday,Days::Tuesday);
    }

    {
    using r = hidden::RegisterNamespace;
    //foo and GetMonday / GetTuesday are all found by argument dependent lookup
    foo(GetMonday(r::val),GetTuesday(r::val));
    }

    return 0;
}
user3125280
  • 2,779
  • 1
  • 14
  • 23