1

Here are my codes for PoC:

a.h: which implements a singleton method to create A instance

#pragma once

class A
{
public:
    int a;

static A& Instance() {
    static A a;
    return a;
}

};

b.h: which declares a function will try to create an A instance inside, and output the address of it.

#pragma once

void test_b();

b.cc: The implementation of test_b

#include "b.h"
#include <iostream>
#include "a.h"


void test_b() {
    auto &a = A::Instance();
    std::cout << "a address in test_b: " << (void *)(&a) << std::endl;
}

and c.cc: test_c which do the same things as test_b, and call test_b and test_c in main to check if the singleton is working.

#include <iostream>
#include "a.h"
#include "b.h"

void test_c() {
    auto &a = A::Instance();
    std::cout << "a address in test_c: " << (void *)(&a) << std::endl;
}


int main() {
    test_b();
    test_c();
    return 0;
}

I using the b.cc to build a shared library libb.so in windows(libb.dll), and using c.cc to create the test_app which links with the shared library.

I've tested the above codes in Linux and Windows(MinGW), but I've got different results.

Under linux the output is like:

a address in test_b: 0x601174
a address in test_c: 0x601174

And under MinGW the output is like:

a address in test_b: 0x7ff87df93070
a address in test_c: 0x7ff731ef30b0

The makefile I used for the build.

Makefile:

test_app: c.cc libb.so
    g++ -o $@ $^ -lb

libb.so: a.h b.cc
    g++ -o $@ -fPIC -shared b.cc

Makefile.mingw

test_app.exe: c.cc libb.dll
        g++ -o $@ $^ -lb -L.

libb.dll: a.h b.cc
        g++ -o $@ -fPIC -shared b.cc

I know implementing Singleton in the header file is not a good practice, but if anyone could help to explain why?

Ji Bin
  • 491
  • 3
  • 9
  • 3
    Your sample code on github involves shared libraries. This is a very important piece of the puzzle that should be part of the question. Your question should be self-contained and answerable without any off-site resources. –  Apr 11 '22 at 15:12
  • Thanks for your advice, I'll paste Makefiles in post – Ji Bin Apr 11 '22 at 15:14
  • 1
    The simplified view here is that shared libraries are not part of the C++ language, and compilers featuring them are forced to make some decisions and assumptions regarding them. Different compilers made different decisions. As far as the language is concerned, this is not really a single C++ program. The closest perspective would be a Frankenstein's monster made of two C++ programs glued together. –  Apr 11 '22 at 15:17
  • Possible duplicate of: https://stackoverflow.com/q/1176427/1387438 – Marek R Apr 11 '22 at 15:25

1 Answers1

1

As far as the language is concerned, the lvalue returned by A::Instance must always refer to the same object within the execution of the program. If a language implementation deviates from that, then it doesn't conform to the standard.

why?

You are using shared libraries which are an extension of the language. As you've witnessed, using this language extension causes the language implementation to deviate from the guarantees given by the standard.

You can work around the issue by defining the instance getter as a non-inline function in a single translation unit, rather than as an inline function. Another way is to use language implementation specific function attributes to control the shared library behaviour as per the documentation of the language implementation (dllexport, dllimport in MSVC).


Sidenote: Singleton pattern requires that the class is encapsulated such that no instances besides the static one can be created. The example is just a static local object and technically not a singleton since the constructor isn't encapsulated.

eerorika
  • 232,697
  • 12
  • 197
  • 326