-
Notifications
You must be signed in to change notification settings - Fork 1
/
README.md
219 lines (184 loc) · 5.84 KB
/
README.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
![windows](https://github.com/volcoma/dynopp/actions/workflows/windows.yml/badge.svg)
![linux](https://github.com/volcoma/dynopp/actions/workflows/linux.yml/badge.svg)
![macos](https://github.com/volcoma/dynopp/actions/workflows/macos.yml/badge.svg)
## dynopp c++14 library for dynamic dispatch and dynamic objects
```c++
// Here we are making an alias of a binder with a key type = std::string
using anybinder = dyno::binder<dyno::anystream, dyno::anystream, std::string>;
//you can also provide a view_type for some more optimizaiton
//using anybinder = dyno::binder<dyno::anystream, dyno::anystream, std::string, std::string_view>;
anybinder binder;
binder.connect("on_some_event", []()
{
// logic
});
binder.connect("on_some_event", [](int arg1, const std::string& arg2)
{
// logic
});
// this will call all the slots. If someone expects more arguments than
// the ones provied an exception will be thrown. Also if
// the types are missmatched an exception will be thrown.
// Type matching is provided by the underlying serializer/deserializer
binder.dispatch("on_some_event", 12, "wooow");
binder.bind("call_with_return", [](const std::string& arg1, float arg2)
{
return 42;
});
int result = binder.call<int>("call_with_return", "somearg1", 12.0f);
// remove binded function
binder.unbind("call_with_return");
// in order to disconnect a slot you need to keep the
// slot_id returned when connecting.
auto slot_id = binder.connect("on_some_event", []()
{
});
// disconnect a slot
binder.disconnect("on_some_event", slot_id);
// You can also create a dynamic object type
// It will behave more or less like a fully dynamic type
using object_rep = dyno::object_rep<dyno::anystream, dyno::anystream, std::string>;
//you can also provide a view_type for some more optimizaiton
//using object_rep = dyno::object_rep<dyno::anystream, dyno::anystream, std::string, std::string_view>;
//or you can change the serialization completely maybe use json or some custom binary format? It's all up to you.
using object_rep = dyno::object_rep<nlohmann::json, nlohmann::json, std::string, hpp::string_view>;
using object = dyno::object<object_rep>;
object obj;
obj["key1"] = 1;
obj["key2"] = "some_string_data";
obj["key3"] = std::vector<std::string>{"str1", "str2", "str3"};
//you can copy it
object inner = obj;
//or nest them inside eachother
obj["key4"] = std::move(inner);
// retrieve fields like so:
int val1 = obj["key1"];
std::string val2 = obj["key2"];
std::vector<std::string> val3 = obj["key3"];
object val4 = obj["key4"];
// this will throw if the key is not present or the value' type cannot be matched
// again type matching is provided by the user. Look below.
int val5 = obj["key5"];
// you can provide a default value if the key doesn't exist.
int val6 = obj["key6"].value_or(4);
// you can also explicitly check
if(obj.has("key6"))
{
}
// or try to get it via an out param
int val{};
if(obj.get("key6", val))
{
}
```
For the binder to work out you need to provide a serialize/deserialize customization point
with archives that you are working with.
The binder example above works with an anystream which is basically a vector<std::any>, but can work
with any other binary stream if you provide a specialization
The anystream is integrated with the library and can be used out of the box.
```c++
namespace dyno
{
template <>
struct archive<anystream, anystream>
{
using oarchive_t = anystream;
using iarchive_t = anystream;
static oarchive_t create_oarchive()
{
return {};
}
static iarchive_t create_iarchive(oarchive_t&& oarchive)
{
return std::move(oarchive);
}
template <typename... Args>
static void pack(oarchive_t& oarchive, Args&&... args)
{
oarchive.storage.reserve(sizeof...(Args));
hpp::for_each(std::forward_as_tuple(std::forward<Args>(args)...),
[&oarchive](auto&& arg) { oarchive << std::forward<decltype(arg)>(arg); });
}
template <typename T>
static bool unpack(iarchive_t& iarchive, T& obj)
{
iarchive >> obj;
return static_cast<bool>(iarchive);
}
static void rewind(iarchive_t& iarchive)
{
iarchive.rewind();
}
};
}
//This for example is making the object work with nlohman's json library
//Check out the examples for more information.
namespace dyno
{
template <typename Key, typename View>
struct object_rep<nlohmann::json, nlohmann::json, Key, View>
{
using key_t = Key;
using view_t = View;
template <typename T>
auto get(const view_t& id, T& val) const -> std::tuple<bool, bool>
{
try
{
T res = impl_.at(key_t(id));
val = std::move(res);
}
catch(const nlohmann::json::out_of_range&)
{
return std::make_tuple(false, false);
}
catch(...)
{
return std::make_tuple(true, false);
}
return std::make_tuple(true, true);
}
auto get(const view_t& id, object_rep& val) const -> std::tuple<bool, bool>
{
return get(id, val.impl_);
}
template <typename T>
auto set(const view_t& id, T&& val) -> std::enable_if_t<!std::is_same<std::decay_t<T>, object_rep>::value>
{
impl_[key_t(id)] = std::forward<T>(val);
}
template <typename T>
auto set(const view_t& id, T&& val) -> std::enable_if_t<std::is_same<std::decay_t<T>, object_rep>::value>
{
impl_[key_t(id)] = val.impl_;
}
bool remove(const view_t& id)
{
impl_.erase(Key(id));
return true;
}
bool has(const view_t& id) const
{
return impl_.find(key_t(id)) != std::end(impl_);
}
bool empty() const
{
return impl_.empty();
}
auto& get_impl()
{
return impl_;
}
const auto& get_impl() const
{
return impl_;
}
nlohmann::json impl_;
};
}
```
### Performance
Keep in mind that this is a purely dynamic dispatch and serialization/deserialization is involved.
If you provide a fast-enough serializer/deserializer you can even speed this up. The library provides
an 'anystream' as shown in the examples, which is basically a vector<std::any>, with any itself having a small size optimization
makes life better.