Are C++ events deprecated?

I was looking into C++ events which can be declared using DECLARE_EVENT.
I was looking into the source code of how they work. This is the declaration of DECLARE_EVENT:

/**
 * Declares a multicast delegate that is meant to only be activated from OwningType
 * NOTE: This behavior is not enforced and this type should be considered deprecated for new delegates, use normal multicast instead
 */
#define DECLARE_EVENT( OwningType, EventName ) FUNC_DECLARE_EVENT( OwningType, EventName, void )

Also in declaration of FUNC_DECLARE_EVENT you can see:

/**
 * Declares a multicast delegate that is meant to only be activated from OwningType
 *
 * @note: This behavior is not enforced and this type should be considered deprecated for new delegates, use normal multicast instead
 */
#define FUNC_DECLARE_EVENT( OwningType, EventName, ReturnType, ... ) \
	class EventName : public TMulticastDelegate<ReturnType(__VA_ARGS__)> \
	{ \
		friend class OwningType; \
	};

Does this mean that events as a concept in C++ are deprecated? Because multicast delegates do not replace events (anyone can Broadcast on a multicast delegate).

Also, if they are deprecated, I don’t see the docs mentioning that:

1 Like

Interesting. I’m not sure why would they be considered deprecated. Events fit well for plugins, or any standalone system for that matter.

Do you specifically want the enforced behaviour described with OwningType?

Yes. I want the type which declares the event be the only type that can broadcast it.

If you declare the multicast event instance in the private section of your header, then only the owner can actually broadcast on it.

(Access control works for static members too, btw.)

1 Like

I don’t think that’s true. Have you tried it yourself? I tried the example in the events doc page and I could call Broadcast on the event in other classes.

Also if you look at FUNC_DECLARE_EVENT, it inherits from TMulticastDelegate which reinforces my guess that there’s no restriction on caller/broadcaster when you declare events.

Are you talking about C++ or Blueprint access? My answer was specific to C++.

If you declare the event as blueprint accessible, it will be accessible from blueprint. If you don’t want that, restrict blueprint access. (But note the “This behavior is not enforced” bit in the docs even for the old kind.)

In C++, you cannot access an instance member that’s private from outside the declaring class.

DECLARE_MULTICAST_DELEGATE(FSomeEvent)

UCLASS()
class MINERSHOOTER_API AMinerShooterGameModeBase : public AGameModeBase
{
	GENERATED_BODY()
public:

private:
	FSomeEvent MyEvent;
};
void AMinerPlayerController::DoAThing(class AMinerShooterGameModeBase* gmb)
{
    gmb->MyEvent.Broacast();
}
2>MinerPlayerController.cpp(26): error C2248: 'AMinerShooterGameModeBase::MyEvent': cannot access private member declared in class 'AMinerShooterGameModeBase'

I’m talking about C++.

The point is, if you declare that as private, then nobody can subscribe to that event which makes it useless.

The way you’re supposed to declare an event is like this:

public:
/** Broadcasts whenever the layer changes */
DECLARE_EVENT( FLayerViewModel, FChangedEvent )
FChangedEvent& OnChanged() { return ChangedEvent; }
private:
/** Broadcasts whenever the layer changes */
FChangedEvent ChangedEvent;

As you can see, the event is declared as private and an accessor function is provided for external classes to subscribe to the event.

Now external classes can use OnChanged, and call Add like this:
LayerViewModelInstance->OnChanged().Add(YourFunctionHere);

but at the same time, they can also call Broadcast like this:

LayerViewModelInstance->OnChanged().Broadcast();

which is the problem.

I think you’re already aware, but worth pointing out that you never had the enforcement that you want for events, either, so you’re asking for some behavior that is net new.

You need to forward subscription, for example using a member function that’s public. If you do this a lot, you could even use a (template) wrapper around the event, that exposes the functions you want to be public, but makes access to the other functions gated on being a friend or something.

template<typename T> HidingWrapper : public T {
    public:
    private:
        void Broadcast(); // hide whatever functions you want to hide
};

    public:
        HidingWrapper<FMyEvent> &OnChanged() {
            return reinterpret_cast<HidingWrapper<FMyEvent> &>(TheEvent);
        }
    private:
        FMyEvent TheEvent;
1 Like

That sucks. I thought it was possible because the docs specifically say that this was the case:

Here is the text from the docs which I linked in my first post:

Events are very similar to multi-cast delegates. However, while any class can bind events, only the class that declares the event may invoke the event’s Broadcast , IsBound , and Clear functions. This means event objects can be exposed in a public interface without worrying about giving external classes access to these sensitive functions.