views:

345

answers:

2

I am trying to do the following: Obtain the address of a member function from a class that was locally defined within a function.

class ConnectionBase
{
};

template class<EventType, SinkType>
class ConnectionImpl : public ConnectionBase
{
public:
typedef void (SinkType::*EventCallback)(EventType const&);
};


template<class EventType>
class Source
{
    template <class SinkType>
    boost::shared_ptr<ConnectionBase> setupCallback(typename ConnectionImpl<EventType, SinkType>::EventCallback func, SinkType* sink)
    {
    // do the actual connecting.
    }
};

class SomeClass
{
public:
    void someFunction(int const& event){}
}

class SomeUnitTest
{
public:
    void someTest()
    {
        class NestedClass 
        {
        public:
            void someFunction(int const& event){}
        };

       NestedClass nc;

       //Try#1 - This does not work
       setupCallback<int, NestedClass>(&NestedClass::someFunction, &nc);

       //Try #2 - This also does not work
       setupCallback<int, NestedClass>(&SomeUnitTest::someTest::NestedClass::someFunction, &nc);

       //Try #3 - Following the GCC error output, I tried this
       setupCallback<int, NestedClass>(&SomeUnitTest::someTest()::NestedClass::someFunction, &nc);

       SomeClass sc;

       //This works fine, as expected
       setupCallback<int, SomeClass>(&SomeClass::someFunction, &sc);

    }
};

Try #2 and #3 utterly confuse GCC, it has no idea what I am trying to do. Try #1 produces a more helpful error message saying no setupCallback exists that takes the form "setupCallback(void (SomeUnitTest::someTest()::NestedClass::SomeFunction::*), etc) Which is how try #3 was born.

I can't really find a lot of information about classes defined inside a function, does anyone know the correct syntax for this, and maybe have a resource that discusses this topic?

Ok, it appears this is settled, as both posters have pointed out, local classes have no linkage, it can't work. Now knowing this, I found this article that discusses this, for anyone else that runs into this problem and stumbles across this question: http://www.informit.com/guides/content.aspx?g=cplusplus&amp;seqNum=420

Edit: Clarification of setupCallback(), working example with a more regular class
Edit #2: Updated wording to change "nested" to "local". Added more detail for setupCallback.
Edit #3: Added links to furhter information. Thanks everyone.

+4  A: 

I don't know about the syntax problem, the usual access rules should apply - but there is another problem here if that would work as these member functions have no linkage.
To accept local types at all, setupCallback() would have to be a template function - but template type arguments with no linkage are not allowed.

§3.5/8 says:

Names not covered by these rules have no linkage. Moreover, except as noted, a name declared in a local scope (3.3.2) has no linkage.

Members of local classes are not covered there. §9.3/3 clarifies that:

Member functions of a local class (9.8) have no linkage.

Long story cut short: don't use member functions of a local class as callbacks, use a non-local class instead.

Georg Fritzsche
Defining a class inside a function has been the source of too many headaches in the past, so I've stopped even considering them as there's always some pitfall I forget.
Dietrich Epp
The fact that local classes have no linkage doesn't in any way mean that you can't create pointers to its members. Just like that fact that automatic variables have no linkage does not prevent us from creating pointers to such variables.
AndreyT
The only way linkage can come into play here is if `setupCallback` is actually a *function template* parametrized by the class type. Classes with no linkage cannot be used as template arguments in C++ (see my answer for more details).
AndreyT
I might have put it more clearly, but i didn't mean to imply that you can't take adresses of members of local classes. I was merely pointing out that, syntactical problems aside, it wouldn't work anyway due to no linkage. Why bother with syntactical issues if it can't work anyway?
Georg Fritzsche
@gf: Why wouldn't it work? What importance do you see in *linkage* in this case? There's absolutely no problems with taking the address and calling the member of a class through the pointer *regardless* of linkage (including using it as a callback). Again, the only potential problems with "no linkage" I can guess here is possible problems with template arguments, if templates are involved.
AndreyT
AndreyT, how else then with templates could you name local types anyway? GCC4 fails for me exactly with *"no matching function for call to ..."* in those cases by the way.
Georg Fritzsche
@gf: I don't know. It is obvious that the code in the question is artificial. In the real code the OP might have several versions of `setupCallback` for different class types. Just a wild guess. Actually, if *can* work even without templates and overloads, if two classes are parent and child in the hierarchy.
AndreyT
@gf: If so, then it makes it even more likely that my wild guess about templates was correct.
AndreyT
AndreyT, i fixed my answer to hopefully avoid misunderstandings - but i'm curious, how could it work if local types can't be template type arguments?
Georg Fritzsche
@gf: I don't exactly understand the question. How? For example, hardcode `SomeClass` into the parameter type of `setupCallback` (see my answer for an example), and you should get exactly the same symptoms as the OP describes (or similar). `SomeClass` works, any other unrelated class doesn't (no matter local or not).
AndreyT
AndreyT, i meant the parent-child note, but got it now. The errors for both cases could fulfill the description though i think.
Georg Fritzsche
@gf: By the parent-child note I just meant that member function pointers are *contravariant*. I.e. a pointer to member of base class can be implictly converted to a pointer to member of derived class. So the first parameter type can be matched even without templates and overloading if the classes are related by inheritance. (The second parameter won't work though since it is *covariant*, as opposed to *contravariant*, but that's a different story).
AndreyT
AndreyT, got that already, probably didn't think of it earlier though because there was no inheritance in the question.
Georg Fritzsche
+4  A: 

You fist variant is the correct one as long as the specific matter of taking the address is considered. There are no restrictions on taking the address of member functions of local classes. The proper syntax is the usual

&NestedClass::someFunction

ans that's it. You can try saving it in an intermediate pointer in your code

void (NestedClass::*ptr)() = &NestedClass::someFunction;

and I'm sure your compiler will accept it.

However, the problem I suspect exists in your code has absolutely nothing to do with the proper way of taking the address of a member function. It is rather about the way the first parameter of setupCallback is declared. Since you say it works with &SomeClass::someFunction as the first argument, I'd expect setupCallback to be declared as

void setupCallback(void (SomeClass::*cb)(), SomeClass *p); // one possibility

i.e. it is hardcoded to expect a pointer to a member of SomeClass specifically. You cannot supply a pointer to a member of NestedClass instead. NestedClass is completely unrelated to SomeClass and pointers to members of NestedClass are completely incompatible with pointers to members of SomeClass. This is why it won't compile.

Unless there's something you are not showing us (like setupCallback being a function template maybe? Or overloaded for different parameter types?), what you are trying to do is simply impossible to achieve regardless of how you take the member address, as long as NestedClass remains unrelated to SomeClass. Function setupCallback is designed to work with SomeClass and SomeClass only.

Provide more information about setupCallback. How is it declared?

Note that if the setupCallback is declared as a function template parametrized by class type, as in

template <class T> void setupCallback(void (T::*cb)(), T* p);

then you won't be able to use the local class NestedClass as template argument for parameter T. In this case the fact that your NestedClass has no linkage does indeed come into play. But, again, it has nothing to do with taking the member address, but rather caused by the fact that classes with no linkage cannot be used as template arguments in C++.

AndreyT
I updated the question. I reckon the fact that the local class does not have linkage is indeed the problem here then?
czuger
@czuger: The updated code is still a bit dirty (non-compilable), but generally yes, the fact that local class has no linkage makes it impossible to use it as a template argument. The language specification explicitly say so. That's the root of the problem. As for taking the address: there are no issues with it. Your "Try #1" variant uses the correct syntax.
AndreyT