Suppose we have a code that contains 1000 copies of function with the following form:
function myname_[X](args){
struct somevariable_[X];
//others
}
where [X] is a sequence say 0001, 0002 etc.
I intend to make it cleaner by programmatically generating the function and variable names instead. How can we do this in c++?
You can use templates to generate a compile-time fixed number of c-style functions, each of which can pass a hard-coded argument to your C++ callback. You can use that hard-coded argument to associate the c-style callback with an object.
Here is a minimal working C++14 example that allows to associate a unique C++ std::function
with each of the generated c-style callback. You can initialize that std::function
with a lambda that captures whatever state you need in that particular callback:
#include <iostream>
#include <algorithm>
#include <functional>
class Callbacks {
static constexpr unsigned count = 1000;
static Callbacks* instance;
using CF = void(); // C-style callback type.
using F = std::function<CF>; // C++ stateful callback type.
F callbacks_[count];
CF* c_style_callbacks_[count];
template<unsigned Index>
static void c_style_callback() {
instance->callbacks_[Index]();
}
template<unsigned... Index>
void make_c_style_callbacks(std::integer_sequence<unsigned, Index...>) {
auto initializer_list = {(c_style_callbacks_[Index] = &c_style_callback<Index>)...};
static_cast<void>(initializer_list);
}
public:
Callbacks() {
make_c_style_callbacks(std::make_integer_sequence<unsigned, count>{});
if(instance)
throw; // One instance only please.
instance = this;
}
Callbacks(Callbacks const&) = delete;
Callbacks& operator=(Callbacks const&) = delete;
~Callbacks() noexcept {
instance = 0;
}
CF* register_callback(F f) noexcept {
// Linear search can be improved upon.
auto condition = [](F const& f) { return !f; };
auto index = std::find_if(std::begin(callbacks_), std::end(callbacks_), condition) - std::begin(callbacks_);
if(index < count) {
callbacks_[index] = std::move(f); // Assumes move-assignement is noexcept.
return c_style_callbacks_[index];
}
return 0;
}
void unregister_callback(CF* cf) noexcept {
// Linear search can be improved upon.
auto index = std::find(std::begin(c_style_callbacks_), std::end(c_style_callbacks_), cf) - std::begin(c_style_callbacks_);
if(index < count)
callbacks_[index] = {};
else
throw; // cf has not been found. Programming error.
}
};
Callbacks* Callbacks::instance = 0;
int main() {
Callbacks c;
unsigned n = 0;
auto p0 = c.register_callback([m = n++]() { std::cout << m << '\n'; });
auto p1 = c.register_callback([m = n++]() { std::cout << m << '\n'; });
auto p2 = c.register_callback([m = n++]() { std::cout << m << '\n'; });
p0(); // Outputs 0.
p1(); // Outputs 1.
p2(); // Outputs 2.
c.unregister_callback(p2);
c.unregister_callback(p1);
c.unregister_callback(p0);
}
The solution requires using a bit of global state, which is Callbacks::instance
here. An alternative would be to parametrize c_style_callback
with a reference to an object with linkage (internal or external), which means a global, namespace-scoped or class static object.
If you are using C++11 you will need to use a backport of std::integer_sequence
and std::make_integer_sequence
which arrived in C++14 but don't actually require any C++14-specific features. Example [m = n++]
named captures are also available since C++14, but that is just for demonstration.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.