1

I want to send a signal to each subclass of a class. To achieve this I am using the signals2 library from boost. Because of the usage of the code I need to use a CRTP.

Minimal example, which is included in a header file:

class A {
    static boost::signals2::signal<void()>& getSignal() {
        static boost::signals2::signal<void()> signal;
        return signal;
    }

public:
    static void sendSignal() {
        getSignal()();
    }

protected:
    template <typename slot_t>
    static boost::signals2::connection addSlot(slot_t slot) {
        return _entityToBeDeleted().connect(slot);
    }
};

template <typename concrete_B>
class B : A {
    static boost::signals2::connection _installHandler() {
        return A::addSlot([]() {
            //Do something
        });
    }

    static const boost::signals2::connection connection;
};
template<typename concrete_B>
const boost::signals2::connection B<concrete_B>::connection = _installHandler();

class C : public B<C> {
    //Concrete subclass
};

Some client class, that wants to notify the subclasses makes a call to A::sendSignal, which invokes all slots connected to the signal. What exactly the content of this notification is is irrelevant for the purpose of this question. As you can see, the side effect of the static initializer of B<concrete_B>::connection is needed for this to work, but as the object itself is never used (nor needed) the complete initialization is skipped.

The problem is: The call to _installHandler() is completely optimized away by the compiler (I'm using MSVC with Visual Studio 2017). I verified this by writing an additional test case, which explicitly tests if the connection object is valid. When I used the object this way the code worked and all other test cases passed. When I removed the additional test case the others failed again.

I tried declaring the static variable volatile as suggested in an answer to this question, but it did not help.

How do I prevent the initialization of the the static connection object to be optimized away. Or: How do I ensure a function with sideeffects that is created by template instanciation is always called once at the beginning of the executable, even though the returned object is never actually used; ideally during static initialization.

I thought about using the connection somehow in another static initializer of this class (std::cout << connection.connected() << '\n'; or something like this) that is already safe to be called (because the object this initializes is definitely used), but this seems like a nasty hack.

Edit: I implemented the "hack" and it works flawlessly, nontheless I am interested if there is a way to implement the desired behaviour in a "cleaner" way.

LukeG
  • 638
  • 10
  • 20
  • 1
    Some linkers will skip including an object file in the executable if no other code odr-uses anything defined in that object file... – aschepler Jul 05 '17 at 12:22
  • 1
    Your minimal example should at least contain how the header file is used. Read this: [mcve] – Jabberwocky Jul 05 '17 at 12:26
  • @aschepler I'm aware of that, the question is how would I prevent that if the relevant part of static initialization is not the returned object itself (which is not needed), but a sideeffect of the function (namely the call to `signal::connect`. – LukeG Jul 05 '17 at 12:33
  • It's not clear from just the header file what's in what translation unit that's causing this. – aschepler Jul 05 '17 at 12:41
  • Note that since initializing `B::connection` involves a non-constexpr function call, it can only be done during _dynamic initialization_ phase. – Maxim Egorushkin Jul 05 '17 at 13:50

1 Answers1

1

The members of a class template get instantiated on demand. One possible fix is to refer to B<concrete_B>::connection from B's constructor or destructor:

B() { static_cast<void>(&connection); }
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Altough I know of this trick to mark intentionally unused function parameters I did not think about using it here. Genius. – LukeG Jul 05 '17 at 14:26