Skip to content

Instantly share code, notes, and snippets.

@marler8997
Created November 6, 2025 16:46
Show Gist options
  • Save marler8997/553f9c70834d9983073f8f95b71a4641 to your computer and use it in GitHub Desktop.
Save marler8997/553f9c70834d9983073f8f95b71a4641 to your computer and use it in GitHub Desktop.
C++ Opaque Concrete Pattern
diff --git a/linux/src/Engine.cpp b/linux/src/Engine.cpp
index 41481be07..9a413e412 100644
--- a/linux/src/Engine.cpp
+++ b/linux/src/Engine.cpp
@@ -117,6 +117,24 @@ namespace
std::vector<std::string> slugs;
} global_personal_call_url;
+
+ // defines conversion functions between the "opaque types" used by zig
+ // and the concrete types in C++
+ #define DEFINE_OPAQUE_CONCRETE(Opaque, Concrete) \
+ Opaque* opaque(Concrete* c) { return reinterpret_cast<Opaque*>(c); } \
+ const Opaque* opaque(const Concrete* c) { return reinterpret_cast<const Opaque*>(c); } \
+ Concrete& concrete(Opaque* o) { return *reinterpret_cast<Concrete*>(o); } \
+ const Concrete& concrete(const Opaque* o) { return *reinterpret_cast<const Concrete*>(o); }
+
+ DEFINE_OPAQUE_CONCRETE(StdString, std::string)
+ DEFINE_OPAQUE_CONCRETE(SharedPtrTnCall, std::shared_ptr<tn::Call>)
+ DEFINE_OPAQUE_CONCRETE(SharedPtrTnPeer, std::shared_ptr<tn::Peer>)
+ DEFINE_OPAQUE_CONCRETE(SharedPtrTnCallNotification, std::shared_ptr<tn::CallNotification>)
+ DEFINE_OPAQUE_CONCRETE(ClientAuthSessionOpaque, tn::RestAPI::ClientAuthSession)
+ DEFINE_OPAQUE_CONCRETE(TnCurrentUser, tn::RestAPI::CurrentUser)
+ DEFINE_OPAQUE_CONCRETE(WebrtcVideoFrameBuffer, webrtc::VideoFrameBuffer)
+ DEFINE_OPAQUE_CONCRETE(SharedPtrScreenVideoSink, std::shared_ptr<ScreenVideoSink>)
+
class AppLinux : public tn::SignalerObserver , public tn::CallerObserver {
public:
AppLinux(int event_fd) : m_event_fd(event_fd) { }
@@ -224,7 +242,7 @@ namespace
void OnDeferredCall(std::shared_ptr<tn::Call> call) override {
MediaStatePinned media_state_pinned;
copy_media_state(&media_state_pinned, call->CurrentMediaState());
- onCall(m_event_fd, (SharedPtrTnCall*)&call, &media_state_pinned);
+ onCall(m_event_fd, opaque(&call), &media_state_pinned);
}
void OnTransientCall(std::shared_ptr<tn::Call>) override {
TN_WARN("TODO: OnTransientCall");
@@ -236,13 +254,13 @@ namespace
// TN_WARN("TODO: OnIncomingCall audio={}", audio_only);
// call->Peers() must be accessed on the thread that called us
auto peer = call->Peers().front();
- onIncomingCall(m_event_fd, (SharedPtrTnCall*)&call, (SharedPtrTnPeer*)&peer, (int)audio_only);
+ onIncomingCall(m_event_fd, opaque(&call), opaque(&peer), (int)audio_only);
}
void OnCallWillEnd(std::shared_ptr<tn::Call>) override {
TN_WARN("TODO: OnCallWillEnd");
}
void OnCallEnd(std::shared_ptr<tn::Call> call) override {
- onCallEnd(m_event_fd, (SharedPtrTnCall*)&call);
+ onCallEnd(m_event_fd, opaque(&call));
}
void OnAudioError(std::shared_ptr<tn::Call>, std::shared_ptr<tn::AudioDeviceIDs>, const tn::AudioError&) override {
TN_WARN("TODO: OnAudioError");
@@ -256,6 +274,7 @@ namespace
void OnPeerAdded(std::shared_ptr<tn::Call>, std::shared_ptr<tn::Peer>) override {
TN_WARN("TODO: OnPeerAdded");
+ // onPeerAdded(m_event_fd, opaque(&call), opaque(&peer));
}
void OnPeerStateChange(std::shared_ptr<tn::Call>, std::shared_ptr<tn::Peer>, const tn::CallPeerState&) override {
TN_WARN("TODO: OnPeerStateChange");
@@ -266,7 +285,7 @@ namespace
void OnMediaChange(std::shared_ptr<tn::Call> call, const tn::CallMediaState& state) override {
MediaStatePinned media_state_pinned;
copy_media_state(&media_state_pinned, state);
- onMediaChange(m_event_fd, (SharedPtrTnCall*)&call, &media_state_pinned);
+ onMediaChange(m_event_fd, opaque(&call), &media_state_pinned);
}
void OnCursorColorsChange(std::shared_ptr<tn::Call>, const tn::CallCursorColors&) override {
TN_WARN("TODO: OnCursorColorsChange");
@@ -288,10 +307,8 @@ namespace
void OnMouseEvent(std::shared_ptr<tn::Call> call, std::shared_ptr<tn::Peer> peer, std::shared_ptr<tn::Protocol::MouseEvent> e) override {
onMouse(
m_event_fd,
- (SharedPtrTnCall*)&call,
- (SharedPtrTnPeer*)&peer,
- e->x_ratio(),
- e->y_ratio()
+ opaque(&call), opaque(&peer),
+ e->x_ratio(), e->y_ratio()
);
}
void OnKeyboardEvent(std::shared_ptr<tn::Call>, std::shared_ptr<tn::Peer>, std::shared_ptr<tn::Protocol::KeyboardEvent>) override {
@@ -309,12 +326,12 @@ namespace
void OnCallNotification(std::shared_ptr<tn::Call> call, std::shared_ptr<tn::CallNotification> call_notification) override {
onCallNotification(
- m_event_fd, (SharedPtrTnCall*)&call, (SharedPtrTnCallNotification*)&call_notification
+ m_event_fd, opaque(&call), opaque(&call_notification)
);
}
void OnCallNotificationDismiss(std::shared_ptr<tn::Call> call, std::shared_ptr<tn::CallNotification> call_notification) override {
onCallNotificationDismiss(
- m_event_fd, (SharedPtrTnCall*)&call, (SharedPtrTnCallNotification*)&call_notification
+ m_event_fd, opaque(&call), opaque(&call_notification)
);
}
@@ -385,90 +402,73 @@ size_t std_string_size(const StdString* s)
void shared_ptr_tn_call_ctor_copy(SharedPtrTnCall* dst, const SharedPtrTnCall* src)
{
- const std::shared_ptr<tn::Call>& src_cpp = *(const std::shared_ptr<tn::Call>*)src;
- new (dst) std::shared_ptr<tn::Call>(src_cpp);
+ new (dst) std::shared_ptr<tn::Call>(concrete(src));
}
void shared_ptr_tn_call_ctor_move(SharedPtrTnCall* dst, SharedPtrTnCall* src)
{
- std::shared_ptr<tn::Call>& src_cpp = *(std::shared_ptr<tn::Call>*)src;
- new (dst) std::shared_ptr<tn::Call>(std::move(src_cpp));
+ new (dst) std::shared_ptr<tn::Call>(std::move(concrete(src)));
}
void shared_ptr_tn_call_dtor(SharedPtrTnCall* call)
{
- ((std::shared_ptr<tn::Call>*)call)->~shared_ptr();
+ concrete(call).~shared_ptr();
}
int shared_ptr_tn_call_eql(const SharedPtrTnCall* a, const SharedPtrTnCall* b)
{
- std::shared_ptr<tn::Call>& a_cpp = *(std::shared_ptr<tn::Call>*)a;
- std::shared_ptr<tn::Call>& b_cpp = *(std::shared_ptr<tn::Call>*)b;
- return a_cpp.get() == b_cpp.get();
+ return concrete(a).get() == concrete(b).get();
}
void shared_ptr_tn_peer_ctor_copy(SharedPtrTnPeer* dst, const SharedPtrTnPeer* src)
{
- const std::shared_ptr<tn::Peer>& src_cpp = *(const std::shared_ptr<tn::Peer>*)src;
- new (dst) std::shared_ptr<tn::Peer>(src_cpp);
+ new (dst) std::shared_ptr<tn::Peer>(concrete(src));
}
void shared_ptr_tn_peer_ctor_move(SharedPtrTnPeer* dst, SharedPtrTnPeer* src)
{
- std::shared_ptr<tn::Peer>& src_cpp = *(std::shared_ptr<tn::Peer>*)src;
- new (dst) std::shared_ptr<tn::Peer>(std::move(src_cpp));
+ new (dst) std::shared_ptr<tn::Peer>(std::move(concrete(src)));
}
void shared_ptr_tn_peer_dtor(SharedPtrTnPeer* peer)
{
- ((std::shared_ptr<tn::Peer>*)peer)->~shared_ptr();
+ concrete(peer).~shared_ptr();
}
int shared_ptr_tn_peer_has_value(const SharedPtrTnPeer* peer)
{
- const std::shared_ptr<tn::Peer>& peer_cpp = *(std::shared_ptr<tn::Peer>*)peer;
- return peer_cpp.get() != 0;
+ return concrete(peer).get() != 0;
}
int shared_ptr_tn_peer_eql(const SharedPtrTnPeer* a, const SharedPtrTnPeer* b)
{
- std::shared_ptr<tn::Peer>& a_cpp = *(std::shared_ptr<tn::Peer>*)a;
- std::shared_ptr<tn::Peer>& b_cpp = *(std::shared_ptr<tn::Peer>*)b;
- return a_cpp.get() == b_cpp.get();
+ return concrete(a).get() == concrete(b).get();
}
const StdString* shared_ptr_tn_peer_full_name_ref(const SharedPtrTnPeer* peer)
{
- const std::shared_ptr<tn::Peer>& peer_cpp = *(std::shared_ptr<tn::Peer>*)peer;
- return (const StdString*)&(peer_cpp->User().full_name);
+ return opaque(&concrete(peer)->User().full_name);
}
void call_notification_ctor_copy(SharedPtrTnCallNotification* dst, const SharedPtrTnCallNotification* src)
{
- const std::shared_ptr<tn::CallNotification>& src_cpp = *(const std::shared_ptr<tn::CallNotification>*)src;
- new (dst) std::shared_ptr<tn::CallNotification>(src_cpp);
+ new (dst) std::shared_ptr<tn::CallNotification>(concrete(src));
}
void call_notification_dtor(SharedPtrTnCallNotification* notification)
{
- ((std::shared_ptr<tn::CallNotification>*)notification)->~shared_ptr();
+ concrete(notification).~shared_ptr();
}
int call_notification_eql(const SharedPtrTnCallNotification* a, const SharedPtrTnCallNotification* b)
{
- std::shared_ptr<tn::CallNotification>& a_cpp = *(std::shared_ptr<tn::CallNotification>*)a;
- std::shared_ptr<tn::CallNotification>& b_cpp = *(std::shared_ptr<tn::CallNotification>*)b;
- return a_cpp.get() == b_cpp.get();
+ return concrete(a).get() == concrete(b).get();
}
CallNotificationKind call_notification_kind(const SharedPtrTnCallNotification* notification)
{
- const std::shared_ptr<tn::CallNotification>& cpp = *(std::shared_ptr<tn::CallNotification>*)notification;
- return zigEnumFromCpp(cpp->Kind());
+ return zigEnumFromCpp(concrete(notification)->Kind());
}
void call_notification_reject(const SharedPtrTnCallNotification* notification)
{
- const std::shared_ptr<tn::CallNotification>& cpp = *(std::shared_ptr<tn::CallNotification>*)notification;
- cpp->Reject();
+ concrete(notification)->Reject();
}
void call_notification_accept(const SharedPtrTnCallNotification* notification)
{
- const std::shared_ptr<tn::CallNotification>& cpp = *(std::shared_ptr<tn::CallNotification>*)notification;
- cpp->Accept();
+ concrete(notification)->Accept();
}
const StdString* call_notification_sender_short_name(const SharedPtrTnCallNotification* notification)
{
- const std::shared_ptr<tn::CallNotification>& cpp = *(std::shared_ptr<tn::CallNotification>*)notification;
- return (const StdString*)&cpp->Sender()->short_name;
+ return opaque(&concrete(notification)->Sender()->short_name);
}
class WebcamCapturerAdapter : public tn::WebcamCapturerAdapter
@@ -518,7 +518,7 @@ const StdString* lock_personal_call_slug()
{
global_personal_call_url.mutex.lock();
if (global_personal_call_url.slugs.size() == 0) return nullptr;
- return (const StdString*)&global_personal_call_url.slugs.back();
+ return opaque(&global_personal_call_url.slugs.back());
}
void unlock_personal_call_slug()
{
@@ -532,12 +532,12 @@ ClientAuthSessionOpaque* auth_session_create(const char* code_verifier_ptr, size
std::unique_ptr<tn::RestAPI::ClientAuthSession> session = tn::Signaler::Default()->RestClient()->CreateAuthSession(
std::string(code_verifier)
);
- return (ClientAuthSessionOpaque*)session.release();
+ return opaque(session.release());
}
void auth_session_delete(ClientAuthSessionOpaque* session)
{
- delete (tn::RestAPI::ClientAuthSession*)session;
+ delete &concrete(session);
}
void format_auth_session_browser_url(
@@ -545,7 +545,7 @@ void format_auth_session_browser_url(
void* context,
void (*callback)(void*, const char* url, size_t len)
) {
- auto url = ((tn::RestAPI::ClientAuthSession*)session)->BrowserURL().str();
+ auto url = concrete(session).BrowserURL().str();
callback(context, url.data(), url.size());
}
@@ -555,7 +555,7 @@ void auth_session_exchange(ClientAuthSessionOpaque* session, int event_fd, const
// NOTE: auth_code is potentially sensitive data we probably don't want to log
// TN_ERROR("auth_session_exchange auth_code='{}'", auth_code);
- ((tn::RestAPI::ClientAuthSession*)session)->Exchange(auth_code, [event_fd](
+ concrete(session).Exchange(auth_code, [event_fd](
std::unique_ptr<tn::RestAPI::CurrentUser> user,
const tn::HTTP::Error& err
) {
@@ -593,11 +593,11 @@ void signaler_sign_out()
void tn_current_user_delete(TnCurrentUser* user)
{
- delete (tn::RestAPI::CurrentUser*)user;
+ delete &concrete(user);
}
const StdString* tn_current_user_auth_token_ref(const TnCurrentUser* user)
{
- return (const StdString*)&((tn::RestAPI::CurrentUser*)user)->auth_token;
+ return opaque(&concrete(user).auth_token);
}
class CallJoiner
@@ -634,14 +634,13 @@ void join_call_slug_std_string(const StdString* slug_opaque)
void reject_call(SharedPtrTnCall* call)
{
- std::shared_ptr<tn::Call>& call_cpp = *(std::shared_ptr<tn::Call>*)call;
- global.caller->Reject(call_cpp);
+ global.caller->Reject(concrete(call));
}
void accept_call(SharedPtrTnCall* call)
{
- std::shared_ptr<tn::Call>& call_cpp = *(std::shared_ptr<tn::Call>*)call;
+ std::shared_ptr<tn::Call> call_cpp = concrete(call);
global.caller->Accept(call_cpp, [call_cpp](const tn::Caller::Error& error) {
- onAcceptCallComplete(global.app->get_event_fd(), (SharedPtrTnCall*)&call_cpp, zigEnumFromCpp(error.Type()));
+ onAcceptCallComplete(global.app->get_event_fd(), opaque(&call_cpp), zigEnumFromCpp(error.Type()));
});
}
@@ -652,12 +651,10 @@ void hangup()
void call_set_mic_enabled(const SharedPtrTnCall* call, int enabled)
{
- const std::shared_ptr<tn::Call>& call_cpp = *(const std::shared_ptr<tn::Call>*)call;
- call_cpp->EnableAudio(enabled);
+ concrete(call)->EnableAudio(enabled);
}
void call_screen_capture_start(const SharedPtrTnCall* call, XdgSessionType session_type, int epoll_fd)
{
- const std::shared_ptr<tn::Call>& call_cpp = *(const std::shared_ptr<tn::Call>*)call;
tn::XdgSessionType session_type_tn = [&]() -> tn::XdgSessionType {
switch (session_type) {
case XdgSessionType::Unspecified: return tn::XdgSessionType::Unspecified;
@@ -666,22 +663,19 @@ void call_screen_capture_start(const SharedPtrTnCall* call, XdgSessionType sessi
case XdgSessionType::Tty: return tn::XdgSessionType::Unspecified;
}
}();
- call_cpp->ScreenCapturer()->Start(tn::VideoCapturerDesktopStart(session_type_tn, epoll_fd), tn::ScreenSource(0));
+ concrete(call)->ScreenCapturer()->Start(tn::VideoCapturerDesktopStart(session_type_tn, epoll_fd), tn::ScreenSource(0));
}
void call_screen_capture_stop(const SharedPtrTnCall* call)
{
- const std::shared_ptr<tn::Call>& call_cpp = *(const std::shared_ptr<tn::Call>*)call;
- call_cpp->ScreenCapturer()->Stop();
+ concrete(call)->ScreenCapturer()->Stop();
}
void call_broadcast_mouse_exited(const SharedPtrTnCall* call)
{
- const std::shared_ptr<tn::Call>& call_cpp = *(const std::shared_ptr<tn::Call>*)call;
- call_cpp->BroadcastUnbuffered(tn::Protocol::MouseEvent::MouseExited());
+ concrete(call)->BroadcastUnbuffered(tn::Protocol::MouseEvent::MouseExited());
}
void call_broadcast_mouse_pos(const SharedPtrTnCall* call, float x, float y)
{
- const std::shared_ptr<tn::Call>& call_cpp = *(const std::shared_ptr<tn::Call>*)call;
- call_cpp->BroadcastUnbuffered(tn::Protocol::MouseEvent::MouseMoved(x, y));
+ concrete(call)->BroadcastUnbuffered(tn::Protocol::MouseEvent::MouseMoved(x, y));
}
void screen_video_sink_new(SharedPtrScreenVideoSink* sink, int event_fd)
@@ -691,43 +685,39 @@ void screen_video_sink_new(SharedPtrScreenVideoSink* sink, int event_fd)
void screen_video_sink_delete(SharedPtrScreenVideoSink* sink)
{
- ((std::shared_ptr<ScreenVideoSink>*)sink)->~shared_ptr();
+ concrete(sink).~shared_ptr();
}
void screen_video_sink_add(const SharedPtrScreenVideoSink* sink, const SharedPtrTnPeer* peer)
{
- const std::shared_ptr<ScreenVideoSink>& sink_cpp = *((std::shared_ptr<ScreenVideoSink>*)sink);
- const std::shared_ptr<tn::Peer>& peer_cpp = *(std::shared_ptr<tn::Peer>*)peer;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// TODO: add TargetResolution
- peer_cpp->AddVideoSink(sink_cpp, tn::Peer::VideoSource::Screen);
+ concrete(peer)->AddVideoSink(concrete(sink), tn::Peer::VideoSource::Screen);
}
void screen_video_sink_remove(const SharedPtrScreenVideoSink* sink, const SharedPtrTnPeer* peer)
{
- const std::shared_ptr<ScreenVideoSink>& sink_cpp = *((std::shared_ptr<ScreenVideoSink>*)sink);
- const std::shared_ptr<tn::Peer>& peer_cpp = *(std::shared_ptr<tn::Peer>*)peer;
- peer_cpp->RemoveVideoSink(sink_cpp, tn::Peer::VideoSource::Screen);
+ concrete(peer)->RemoveVideoSink(concrete(sink), tn::Peer::VideoSource::Screen);
}
void webrtc_video_frame_buffer_addref(WebrtcVideoFrameBuffer* buffer)
{
- ((webrtc::VideoFrameBuffer*)buffer)->AddRef();
+ concrete(buffer).AddRef();
}
void webrtc_video_frame_buffer_unref(WebrtcVideoFrameBuffer* buffer)
{
- ((webrtc::VideoFrameBuffer*)buffer)->Release();
+ concrete(buffer).Release();
}
int webrtc_video_frame_buffer_width(WebrtcVideoFrameBuffer* buffer)
{
- return ((webrtc::VideoFrameBuffer*)buffer)->width();
+ return concrete(buffer).width();
}
int webrtc_video_frame_buffer_height(WebrtcVideoFrameBuffer* buffer)
{
- return ((webrtc::VideoFrameBuffer*)buffer)->height();
+ return concrete(buffer).height();
}
WebrtcVideoFrameBufferType webrtc_video_frame_buffer_type(WebrtcVideoFrameBuffer* buffer)
{
- switch (((webrtc::VideoFrameBuffer*)buffer)->type()) {
+ switch (concrete(buffer).type()) {
case webrtc::VideoFrameBuffer::Type::kNative: return WebrtcVideoFrameBufferType::kNative;
case webrtc::VideoFrameBuffer::Type::kI420: return WebrtcVideoFrameBufferType::kI420;
case webrtc::VideoFrameBuffer::Type::kI420A: return WebrtcVideoFrameBufferType::kI420A;
@@ -748,8 +738,7 @@ void webrtc_video_frame_buffer_get_i420(
uint8_t const** out_v_ptr,
int* out_v_stride
) {
- webrtc::VideoFrameBuffer* buffer_cpp = (webrtc::VideoFrameBuffer*)buffer;
- const webrtc::I420BufferInterface* i420 = buffer_cpp->GetI420();
+ const webrtc::I420BufferInterface* i420 = concrete(buffer).GetI420();
*out_y_ptr = i420->DataY();
*out_y_stride = i420->StrideY();
*out_u_ptr = i420->DataU();
@@ -761,12 +750,12 @@ void webrtc_video_frame_buffer_get_i420(
void media_state_destroy(MediaStatePinned* state)
{
- ((std::shared_ptr<tn::Peer>*)&state->peer_sharing_their_screen_pinned)->~shared_ptr();
+ concrete(&state->peer_sharing_their_screen_pinned).~shared_ptr();
}
void media_state_move(MediaStatePinned* dst, MediaStatePinned* src)
{
- *(std::shared_ptr<tn::Peer>*)&dst->peer_sharing_their_screen_pinned = std::move(
- *(std::shared_ptr<tn::Peer>*)&src->peer_sharing_their_screen_pinned
+ concrete(&dst->peer_sharing_their_screen_pinned) = std::move(
+ concrete(&src->peer_sharing_their_screen_pinned)
);
dst->peer_screen_width = src->peer_screen_width;
dst->peer_screen_height = src->peer_screen_height;
diff --git a/shared/vendor/webrtc/src b/shared/vendor/webrtc/src
--- a/shared/vendor/webrtc/src
+++ b/shared/vendor/webrtc/src
@@ -1 +1 @@
-Subproject commit 07c4b33b53bbd4399fe0f4cc94d64c2ddb5d0dbd
+Subproject commit 07c4b33b53bbd4399fe0f4cc94d64c2ddb5d0dbd-dirty
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment