2

I'm trying to access a private static method from a friend class but get a LINK error. accessing any other privte non-static members and methods works just fine.

NOTE: This question is not on a general unresolved symbol (the DLL does contain the implementation, and we do link with its library). The symbol DOES exist in the DLL but it is exposed as 'private' (if I check it in the dependency-walker) while the linker is looking for 'public' (if I check the decorated name reported by the linker). The problem as I see it is that the linker seems to be ignoreing the friendship when trying to resolve a private static method.

e.g:

class CA
{
    friend class CMyFriend;
private:
    static void TestedFunc();
};

class CMyFriend
{
public:
    static void Tester()
    {
        CA::TestedFunc();
    }
};
Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
Roi
  • 73
  • 5
  • You're declaring that the class `CA::CMyFriend` is a friend of `CA`. The solution is *forward declaration* of `CMyFriend`, or using the scoping as `::CMyFriend`. – Some programmer dude Apr 25 '18 at 06:10
  • 1
    @RSahu I disagree with this being marked as duplicate. This is a very specific case that I do not see being addressed in any answer of the linked question. – Max Vollmer Apr 25 '18 at 06:16
  • 1
    @Someprogrammerdude `friend class` declaration does not require forward declaration. linker error caused by fact that static method wasn't defined in any compilation module – Swift - Friday Pie Apr 25 '18 at 06:24
  • 1
    @Someprogrammerdude I tried to use forward declaration, but it didn't work. Note that accessing private non-static members does work for me. Additional information: I am using VS-2010. – Roi Apr 25 '18 at 06:25
  • @Swift The static method is defined (I see it in the exported symbols of the DLL). The problem is that it is exported as private, while the linker tries to resolve it only as a public. – Roi Apr 25 '18 at 06:28
  • 1
    exporting private members is requiring dirty tricks. Cannot be linked automatically unless consumer would rely on a UB (breach of one definition rule). There was such question on SO https://stackoverflow.com/questions/6398090/is-it-possible-to-invoke-an-exported-private-method-in-c – Swift - Friday Pie Apr 25 '18 at 06:28
  • What happens if you inline the definition of the private static. Does it work then? – jxh Apr 25 '18 at 06:34
  • @jxh I tried to inline it (I used the inlined keywork as well, and enabled it in the project settings) but it didn't work (probably due to the fact that the class has the dllimport in its declaration. Anyway this is not an acceptable solution, as I would not wish to expose the implementation in my .h file, not to mention it requires me to expose internal .h files. – Roi Apr 25 '18 at 06:43
  • 1
    The workaround I have in mind is to create a new private static method that acts as a proxy to the one you actually want to call. The new private static method would be inlined to call the one that is implemented in the DLL. – jxh Apr 25 '18 at 07:53

2 Answers2

1

This might be a crux, but you could redesign your classes a bit to use a private static proxy object with a public method:

header

class CA
{
    friend class CMyFriend;
private:
    class CAProxy final {
    friend class CA;
    private:
        CAProxy(){}
    };
    static CAProxy proxy;
    static void TestedFunc();
public:
    static void TestedFunc(const CAProxy& proxy); // This just calls TestedFunc();
};

implementation

CA::CAProxy CA::proxy{};

void CA::TestedFunc(const CAProxy& proxy) {
    TestedFunc();
}

void CA::TestedFunc() {
    // your code...
}

caller

class CMyFriend
{
public:
    static void Tester()
    {
        CA::TestedFunc(CA::proxy);  // This will call the public method
    }
};

Because the public method needs a reference to an object that cannot be instantiated by anyone except CA itself, and because the static instance CA::proxy is private and only accessible to friends, only friends can call this public method.

Max Vollmer
  • 8,412
  • 9
  • 28
  • 43
  • I believe this solution will work, as there is neither decoration nor dllimport on data members. The solution I went for was removing this function from being private in my SDK's API class, and moved it to be public in a utility which is exposed in my internal (non-SDK) API. That way I could call it from my unit-testing code. I still wonder if the behavior I described is part of the C++ standard, or a bug in the compiler? – Roi Apr 25 '18 at 08:46
  • @Roi dymanic linking is not covered by standard. thechnically it is, but very roundabout way, because when you compile dll, you create a .lib file your program should be linked with. That's import library, it is generated by compiler in inplementation-defined way, but how compiler would link it with program is well defined. There are command-line tools to inspect exported functions in dll and simbols in .libs. – Swift - Friday Pie Apr 25 '18 at 15:21
0

Honestly, I think your code should work.

I tested it on online compiler:

#include <iostream>
#include <string.h>

class CA {
    friend class CMyFriend;

private:
    static int num_;
    static void TestedFunc() {
        std::cout << num_ << std::endl;
    }
};

int CA::num_ = 99;

class CMyFriend {
public:
    static void Tester() {
        CA::TestedFunc();
    }
};

int main(int argc, char *argv[]){
    CMyFriend::Tester();
    return 0;
}

And it works! Screenshot

Because it's been a long time, maybe the bug has been fixed, or it's a wrong question.