Skip to content

Commit a71d975

Browse files
committed
Implement Castable
1 parent 75ecfab commit a71d975

7 files changed

Lines changed: 82 additions & 2 deletions

File tree

src/sentry/android/android_scope.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
namespace sentry::android {
66

77
class AndroidScope : public SentryScopeImpl {
8+
SENTRY_CASTABLE(AndroidScope, SentryScopeImpl);
9+
810
private:
911
Object *android_plugin = nullptr;
1012
int32_t handle = 0;

src/sentry/castable.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#pragma once
2+
3+
#include <type_traits>
4+
5+
namespace sentry {
6+
7+
// Casts between related classes without the cost of dynamic_cast.
8+
// Declare with SENTRY_CASTABLE in every participating class, naming Castable
9+
// as the base in the root class.
10+
class Castable {
11+
public:
12+
struct TypeInfo {
13+
const TypeInfo *parent;
14+
};
15+
16+
static constexpr TypeInfo type_info{ nullptr };
17+
18+
// Returns true if p_from is a T, or derives from T. Null is safe and never matches.
19+
template <typename T>
20+
static bool is_class(const Castable *p_from) {
21+
static_assert(std::is_same_v<typename T::castable_self, T>,
22+
"T must declare SENTRY_CASTABLE");
23+
if (!p_from) {
24+
return false;
25+
}
26+
const TypeInfo *t = p_from->get_type_info();
27+
while (t) {
28+
if (t == &T::type_info) {
29+
return true;
30+
}
31+
t = t->parent;
32+
}
33+
return false;
34+
}
35+
36+
// Returns p_from as T, or null if it isn't one. Null is safe.
37+
template <typename T>
38+
static T *cast_to(Castable *p_from) {
39+
return is_class<T>(p_from) ? static_cast<T *>(p_from) : nullptr;
40+
}
41+
42+
// Returns p_from as T, or null if it isn't one. Null is safe.
43+
template <typename T>
44+
static const T *cast_to(const Castable *p_from) {
45+
return is_class<T>(p_from) ? static_cast<const T *>(p_from) : nullptr;
46+
}
47+
48+
virtual ~Castable() = default;
49+
50+
protected:
51+
virtual const TypeInfo *get_type_info() const = 0;
52+
};
53+
54+
} //namespace sentry
55+
56+
// Adds type info to a castable class. Add at the top of the class body.
57+
#define SENTRY_CASTABLE(m_class, m_base) \
58+
public: \
59+
using castable_self = m_class; \
60+
static constexpr ::sentry::Castable::TypeInfo type_info{ &m_base::type_info }; \
61+
\
62+
protected: \
63+
virtual const ::sentry::Castable::TypeInfo *get_type_info() const override { \
64+
static_assert(std::is_base_of_v<m_base, m_class>, \
65+
#m_base " must be a base of " #m_class "."); \
66+
return &type_info; \
67+
} \
68+
\
69+
private:

src/sentry/disabled/disabled_scope.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
namespace sentry {
66

77
class DisabledScope : public SentryScopeImpl {
8+
SENTRY_CASTABLE(DisabledScope, SentryScopeImpl);
9+
810
public:
911
virtual void set_context(const String &p_key, const Dictionary &p_value) override {}
1012
virtual void set_tag(const String &p_key, const String &p_value) override {}

src/sentry/javascript/javascript_scope.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ namespace sentry::javascript {
99
// yield a DisabledScope instead.
1010
// See JavaScriptSDK::create_scope().
1111
class JavaScriptScope : public SentryScopeImpl {
12+
SENTRY_CASTABLE(JavaScriptScope, SentryScopeImpl);
13+
1214
private:
1315
JSObjectPtr js_obj;
1416

src/sentry/javascript/javascript_sdk.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ namespace {
118118
// Scope creation can fall back to DisabledScope, and null falls back to capture without passed scope.
119119
inline JSObjectPtr _get_scope_object(const Ref<SentryScope> &p_scope) {
120120
ERR_FAIL_COND_V(p_scope.is_null(), nullptr);
121-
JavaScriptScope *js_scope = dynamic_cast<JavaScriptScope *>(p_scope->get_implementation());
121+
JavaScriptScope *js_scope = Castable::cast_to<JavaScriptScope>(p_scope->get_implementation());
122122
return js_scope ? js_scope->get_js_object() : nullptr;
123123
}
124124

src/sentry/native/native_scope.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ namespace sentry::native {
88

99
// Thin wrapper around sentry-native scope used for SentryScope implementation on Windows and Linux.
1010
class NativeScope : public SentryScopeImpl {
11+
SENTRY_CASTABLE(NativeScope, SentryScopeImpl);
12+
1113
private:
1214
sentry_scope_t *_scope;
1315

src/sentry/sentry_scope_impl.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include "sentry/castable.h"
34
#include "sentry/level.h"
45
#include "sentry/sentry_breadcrumb.h"
56
#include "sentry/sentry_user.h"
@@ -17,7 +18,9 @@ namespace sentry {
1718
// Kept as a pure C++ class instead of a Godot class to avoid ClassDB
1819
// registration and reduce overhead.
1920
// Lifetime governed by SentryScope.
20-
class SentryScopeImpl {
21+
class SentryScopeImpl : public Castable {
22+
SENTRY_CASTABLE(SentryScopeImpl, Castable);
23+
2124
public:
2225
virtual void set_context(const String &p_key, const Dictionary &p_value) = 0;
2326
virtual void set_tag(const String &p_key, const String &p_value) = 0;

0 commit comments

Comments
 (0)