For example, I have such function which performs some useful work (for event-driven simulation):
int function()
{
do_useful_work();
return 0;
}
If I need do measurements of performance of this useful_work I should do:
int function()
{
count_time(time_before);
count_X_metrics(X_before);
do_useful_work();
count_time(time_after);
count_X_metrics(X_after);
return 0;
}
This approach makes code more clumsy. Is there a way, patters to do these countings outside of int function() to make code clearer?
You could create your own decorator like the following:
#include<functional>
#include <iostream>
void count_time() {};
void count_X_metrics() {};
void decorator(std::function<void()> work)
{
count_time();
count_X_metrics();
work();
count_time();
count_X_metrics();
}
void do_work_1() {
std::cout << "Hello, World 1!" << std::endl;
}
void do_work_2() {
std::cout << "Hello, World 2!" << std::endl;
}
int main() {
decorator(do_work_1);
decorator(do_work_2);
}
Edit: I'm not sure how your count_time and count_X_metrics functions work, but if you need something more complicated, or a way to keep track of state, you can create an object that will do that work for you. This is certainly different than you need, but hopefully it conveys the point I am trying to make:
#include<functional>
#include <iostream>
int current_time() { return 0; }
int x_metric() { return 0; }
class Timer {
public:
void time(std::function<void()> work) {
// Capture state before
int starttime = current_time();
int startmetric = x_metric();
work();
// Capture state after
int endtime = current_time();
int endmetric = x_metric();
// Update results
ellapsed = endtime - starttime;
metric = endmetric - startmetric;
// Possibly do something with the metrics here.
// ...
}
int get_ellapsed() { return ellapsed; }
int get_metric() { return metric; }
private:
int ellapsed;
int metric;
};
void do_work_1() {
std::cout << "Hello, World 1!" << std::endl;
}
void do_work_2() {
std::cout << "Hello, World 2!" << std::endl;
}
int main() {
Timer t;
t.time(do_work_1);
// Possibly do something with the metrics here.
// cout << t.get_ellapsed();
t.time(do_work_2);
}
Related
Does this code architecture cause memory leakage by not freeing m_func?
And could this be tempered with if this code would be executed at a closed server? Like finding the address of the pointer and replacing the code of the function pointee with malicious code? If so how could I solve this?
#include <iostream>
template <typename Func>
struct endpoint_t {
void* m_func;
endpoint_t(Func&& func) : m_func((void*) func) {}
auto execute() {
return ((Func*) m_func)();
}
};
int hello_world() {
std::cout << "Hello World! \n";
return 0;
}
int main() {
endpoint_t end(hello_world);
end.execute();
}
Edit:
This is the actual goal of the code: To store multiple endpoint functions inside a vector.
#include <vector>
#include <iostream>
template <typename Func>
struct endpoint_t {
void* m_func;
endpoint_t(Func&& func) : m_func((void*) func) {}
auto execute() {
return ((Func*) m_func)();
}
};
int hello_world() {
std::cout << "Hello World! \n";
return 0;
}
int hello_world2() {
std::cout << "Hello World 2! \n";
return 0;
}
int main() {
std::vector<endpoint_t<???>> array;
array.push_back(hello_world);
array.push_back(hello_world2);
}
Assuming the prototypes of all your 'hello world' functions is the same (int return value, no parameter), you don't need templates at all. Just store a function pointer.
typedef int (*Func_t)();
int hello_world() {
std::cout << "Hello World! \n";
return 0;
}
int hello_world2() {
std::cout << "Hello World 2! \n";
return 0;
}
int main() {
std::vector<Func_t> array;
array.push_back(&hello_world);
array.push_back(&hello_world2);
}
Assuming that the prototypes do differ, it becomes a wee bit more difficult, but not very much so, thanks to std::function.
int hello_world() {
std::cout << "Hello World! \n";
return 0;
}
int hello_world2(int value) {
std::cout << "Hello World 2! \n";
return 0;
}
int main() {
std::vector<std::function<int ()>> array;
array.push_back(&hello_world);
array.push_back(std::bind(&hello_world2, 2));
}
Please note, that std::bind and lambdas require you to pass any given parameter at the time of binding. You cannot add the parameter later.
At first it seemed clear that I shouldn't be able to do this, but then I discovered that it can be done with free functions.
Here is an example where I pass void() functions from Child to Parent. The parent calls the function when their Frame comes up.
I have figured out the syntax to pass free functions with arguments, but I can't figure out how to pass a member function of Child with an argument.
Please help.
#include <iostream>
#include <vector>
#include <map>
#include <functional>
void f_Free1() { std::cout << "Hi there hello. I'm a free function without arguments."; }
void f_Free2(int i) { std::cout << "Hi there hello. I'm a free function with an argument. It's " << i; }
class Parent
{
std::map<unsigned int, std::vector<std::function<void()>>> Tasks;
protected:
void addTask(unsigned int frame, std::function<void()> task) { Tasks[frame].push_back(task); }
public:
virtual ~Parent() {}
unsigned int Frame = 0;
void tick()
{
if (Tasks.count(Frame))
{
for (auto task : Tasks[Frame])
{
task();
}
}
Frame++;
}
};
class Child : public Parent
{
void f_Child1() { std::cout << "This is a private Child function without arguments. "; }
void f_Child2(int i) { std::cout << "This is a private Child function with an argument. It's " << i; }
public:
Child()
{
addTask(3, f_Free1);
addTask(5, [a = int(4)] () { f_Free2(a); } ); // THIS WORKS!!!
addTask(7, std::function<void()> { std::bind(&Child::f_Child1, this) });
addTask(9, std::function<void()> { std::bind([a = int(4)]() { &Child::f_Child2(a), this) } }); // CAN'T MAKE THIS WORK
}
};
int main()
{
Child child;
for (unsigned int i = 0; i < 12; i++)
{
std::cout << "[" << child.Frame << "]";
child.tick(); // runs tasks whose frames are up
std::cout << std::endl;
}
return 0;
}
Ditch the bind.
Use [this]() { f_Child1(); } and [this]() { f_Child(4); }.
Also, the free version can be just []() { f_Free2(4); }.
std::bind's syntax would be:
std::bind(&f_Free1)
std::bind(&f_Free2, 4)
std::bind(&Child::f_Child1, this)
std::bind(&Child::f_Child2, this, 4)
But lambda is simpler for most people:
&f_Free1 is fine, else [](){ return f_Free1(); }
[](){ return f_Free2(4); }
[this]() { return this->f_Child1(); )
[this]() { return this->f_Child2(4); )
return can be omitted here as functions return void.
this-> can be omitted in lambda.
you might capture more or differently for arguments.
Assume that there is a class which contains some data and calculates some results given queries, and the queries take a relatively large amount of time.
An example class (everything dummy) is:
#include <vector>
#include <numeric>
#include <thread>
struct do_some_work
{
do_some_work(std::vector<int> data)
: _data(std::move(data))
, _current_query(0)
, _last_calculated_result(0)
{}
void update_query(size_t x) {
if (x < _data.size()) {
_current_query = x;
recalculate_result();
}
}
int get_result() const {
return _last_calculated_result;
}
private:
void recalculate_result() {
//dummy some work here
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
_last_calculated_result = std::accumulate(_data.cbegin(), _data.cbegin() + _current_query, 0);
}
std::vector<int> const _data;
size_t _current_query;
int _last_calculated_result;
};
and this can be used in the main code like:
#include <algorithm>
int main()
{
//make some dummy data
std::vector<int> test_data(20, 0);
std::iota(test_data.begin(), test_data.end(), 0);
{
do_some_work work(test_data);
for (size_t i = 0; i < test_data.size(); ++i) {
work.update_query(i);
std::cout << "result = {" << i << "," << work.get_result() << "}" << std::endl;
}
}
}
The above will wait in the main function a lot.
Now, assuming we want to run this querying in a tight loop (say GUI) and only care about about getting a "recent" result quickly when we query.
So, we want to move the work to a separate thread which calculates the results, and updates it, and when we get result, we get the last calculated one. That is, we want to change do_some_work class to do its work on a thread, with minimal changes (essentially find a pattern of changes that can be applied to (mostly) any class of this type).
My stab at this is the following:
#include <vector>
#include <numeric>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <iostream>
struct do_lots_of_work
{
do_lots_of_work(std::vector<int> data)
: _data(std::move(data))
, _current_query(0)
, _last_calculated_result(0)
, _worker()
, _data_mtx()
, _result_mtx()
, _cv()
, _do_exit(false)
, _work_available(false)
{
start_worker();
}
void update_query(size_t x) {
{
if (x < _data.size()) {
std::lock_guard<std::mutex> lck(_data_mtx);
_current_query = x;
_work_available = true;
_cv.notify_one();
}
}
}
int get_result() const {
std::lock_guard<std::mutex> lck(_result_mtx);
return _last_calculated_result;
}
~do_lots_of_work() {
stop_worker();
}
private:
void start_worker() {
if (!_worker.joinable()) {
std::cout << "starting worker..." << std::endl;
_worker = std::thread(&do_lots_of_work::worker_loop, this);
}
}
void stop_worker() {
std::cout << "worker stopping..." << std::endl;
if (_worker.joinable()) {
std::unique_lock<std::mutex> lck(_data_mtx);
_do_exit = true;
lck.unlock();
_cv.notify_one();
_worker.join();
}
std::cout << "worker stopped" << std::endl;
}
void worker_loop() {
std::cout << "worker started" << std::endl;
while (true) {
std::unique_lock<std::mutex> lck(_data_mtx);
_cv.wait(lck, [this]() {return _work_available || _do_exit; });
if (_do_exit) { break; }
if (_work_available) {
_work_available = false;
int query = _current_query; //take local copy
lck.unlock(); //unlock before doing lots of work.
recalculate_result(query);
}
}
}
void recalculate_result(int query) {
//dummy lots of work here
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
int const result = std::accumulate(_data.cbegin(), _data.cbegin() + query, 0);
set_result(result);
}
void set_result(int result) {
std::lock_guard<std::mutex> lck(_result_mtx);
_last_calculated_result = result;
}
std::vector<int> const _data;
size_t _current_query;
int _last_calculated_result;
std::thread _worker;
mutable std::mutex _data_mtx;
mutable std::mutex _result_mtx;
std::condition_variable _cv;
bool _do_exit;
bool _work_available;
};
and the usage is (example):
#include <algorithm>
int main()
{
//make some dummy data
std::vector<int> test_data(20, 0);
std::iota(test_data.begin(), test_data.end(), 0);
{
do_lots_of_work work(test_data);
for (size_t i = 0; i < test_data.size(); ++i) {
work.update_query(i);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cout << "result = {" << i << "," << work.get_result() << "}" << std::endl;
}
}
}
This seems to work, giving the last result, not stopping the main function etc.
But, this looks a LOT of changes are required to add a worker thread to a simple class like do_some_work. Items like two mutexes (one for the worker/main interaction data, and one for the result), one condition_variable, one more-work-available flag and one do-exit flag, that is quite a bit. I guess we don't want an async kind of mechanism because we don't want to potentially launch a new thread every time.
Now, I am not sure if there is a MUCH simpler pattern to make this kind of change, but it feels like there should be. A kind of pattern that can be used to off-load work to a thread.
So finally, my question is, can do_some_work be converted into do_lots_of_work in a much simpler way than the implementation above?
Edit (Solution 1) ThreadPool based:
Using a threadpool, the worker loop can be skipped, we need two mutexes, for result and query. Lock in updating query, Lock in getting result, Both lock in recalculate (take a local copy of a query, and write to result).
Note: Also, when pushing work on the queue, as we do not care about the older results, we can clear the work queue.
Example implementation (using the CTPL threadpool)
#include "CTPL\ctpl_stl.h"
#include <vector>
#include <mutex>
struct do_lots_of_work_with_threadpool
{
do_lots_of_work_with_threadpool(std::vector<int> data)
: _data(std::move(data))
, _current_query(0)
, _last_calculated_result(0)
, _pool(1)
, _result_mtx()
, _query_mtx()
{
}
void update_query(size_t x) {
if (x < _data.size()) {
std::lock_guard<std::mutex> lck(_query_mtx);
_current_query = x;
}
_pool.clear_queue(); //clear as we don't want to calculate any out-date results.
_pool.push([this](int id) { recalculate_result(); });
}
int get_result() const {
std::lock_guard<std::mutex> lck(_result_mtx);
return _last_calculated_result;
}
private:
void recalculate_result() {
//dummy some work here
size_t query;
{
std::lock_guard<std::mutex> lck(_query_mtx);
query = _current_query;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
int result = std::accumulate(_data.cbegin(), _data.cbegin() + query, 0);
{
std::lock_guard<std::mutex> lck(_result_mtx);
_last_calculated_result = result;
}
}
std::vector<int> const _data;
size_t _current_query;
int _last_calculated_result;
ctpl::thread_pool _pool;
mutable std::mutex _result_mtx;
mutable std::mutex _query_mtx;
};
Edit (Solution 2) With ThreadPool and Atomic:
This solution changes the shared variables to atomic, and so we do not need any mutexes and do not have to consider taking/releasing locks etc. This is much simpler and very close to the original class (of course assumes a threadpool type exists somewhere as it is not part of the standard).
#include "CTPL\ctpl_stl.h"
#include <vector>
#include <mutex>
#include <atomic>
struct do_lots_of_work_with_threadpool_and_atomics
{
do_lots_of_work_with_threadpool_and_atomics(std::vector<int> data)
: _data(std::move(data))
, _current_query(0)
, _last_calculated_result(0)
, _pool(1)
{
}
void update_query(size_t x) {
if (x < _data.size()) {
_current_query.store(x);
}
_pool.clear_queue(); //clear as we don't want to calculate any out-date results.
_pool.push([this](int id) { recalculate_result(); });
}
int get_result() const {
return _last_calculated_result.load();
}
private:
void recalculate_result() {
//dummy some work here
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
_last_calculated_result.store(std::accumulate(_data.cbegin(), _data.cbegin() + _current_query.load(), 0));
}
std::vector<int> const _data;
std::atomic<size_t> _current_query;
std::atomic<int> _last_calculated_result;
ctpl::thread_pool _pool;
};
I want to be able to return a function from a class, so that I do not need to if-else through a return type.
I have a class that returns multiple strings. Instead, I want to return multiple functions.
#include <iostream>
class Handler
{
private:
public:
int handleMessage(int code)
{
return code+1;
}
};
void func1();
void func2();
void func3();
int main (int argc, char *argv[])
{
Handler handle;
int code = handle.handleMessage(0);
if(code == 1)
{
func1();
}
return 0;
}
void func1(){ std::cout << "1" << std::endl;}
void func2(){ std::cout << "2" << std::endl;}
void func3(){ std::cout << "3" << std::endl;}
What I want is: That the function handleMessage in the class Handler returns something so that in my main application I do not have to use if-else.
So the main looks like this:
function = handle.handleMessage(0);
And the application will choose which function it will run.
for example:
function = handle.handleMessage(0); //will run func1
function = handle.handleMessage(1); //will run func2
You can modify the member function such that it returns a function pointer, e.g.
using fptr = void (*)();
struct Handler
{
fptr handleMessage (int code)
{
if (code == 0)
return &func1;
else if (code == 1)
return &func2;
else
return &func3;
}
};
This can be invoked as follows
Handler handle;
auto f = handle.handleMessage(0);
f();
Note that the above if-else if-else dispatch isn't ideal. Prefer a data member that stores the function pointers and associates them with a code, e.g. using a std::unordered_map.
Note that when you need to return stateful function objects in the future, this approach will fail. Then, you need to embrace std::function which is able to wrap lambdas with closures or custom types with an operator() overload.
There are several ways to do so, the simplest one, you can use an std::function. In this example we returning a lambda function for each case. You can replace it with the functions you just wrote.
class Handler {
public:
std::function<void()> handleMessage(int code) {
code = code + 1; // ++code or whatever
if (code == X) {
return []() { std::cout << "Cool! I'am x!" << std::endl; };
} else if (code == Y) {
return []() { std::cout << "Cool! I'am x!" << std::endl; };
} else if (...) {
...
} else {
....
}
}
};
Then your main function becomes:
int main (int argc, char *argv[]) {
Handler handle;
const auto func = handle.handleMessage(0);
func();
return 0;
}
You can replace the swith/if case statement by an array storing the different functions, like they mentioned in the comments.
If you dont want to pay the extra virtual function call regarding the usage of an std::function, you can use an alias like the answer below or just the auto keyword:
class Handler {
public:
constexpr auto handleMessage(int code) {
code = code + 1; // ++code or whatever
if (code == X) {
return &func1;
} else if (code == Y) {
return &func2;
} else if (...) {
...
} else {
....
}
}
};
std::function is a powerful tool. The tiny brother is a simple function pointer.
I transformed MCVE respectively to return a function pointer:
#include <iostream>
typedef void (*FuncPtr)();
void func1();
void func2();
void func3();
void funcError();
class Handler
{
private:
public:
FuncPtr handleMessage(int code)
{
switch (code + 1) {
case 1: return &func1;
case 2: return &func2;
case 3: return &func3;
default: return &funcError;
}
}
};
int main (int argc, char *argv[])
{
Handler handle;
FuncPtr pFunc = handle.handleMessage(0);
pFunc();
return 0;
}
void func1(){ std::cout << "1" << std::endl;}
void func2(){ std::cout << "2" << std::endl;}
void func3(){ std::cout << "3" << std::endl;}
void funcError(){ std::cout << "ERROR!" << std::endl;}
Output:
1
Live Demo on coliru
You can return a function with return_type(*function_name)(argument_type1, argument_type2...) so a function that looks like:
double f(int a, int b);
has the name double(*f)(int, int).
Worth mentioning is C++11's std::function which requires the <functional> header. It has a more intuitive usage: std::function<double(int, int)> but also adds a bit of overhead.
I would also like to suggest the usage of C++17's std::optional as for the case when the variable code goes out of bounds. This implementation requires the <optional> header.
std::optional<void(*)()> handleMessage(int code){
switch (code) {
case 0: return std::optional(func1);
case 1: return std::optional(func2);
case 2: return std::optional(func3);
}
return std::nullopt; //empty
}
usage in main looks like the following:
Handler handle;
auto func = handle.handleMessage(0);
if (func.has_value()) {
func.value()();
}
as this allows to check if func.has_value() which is quite convenient.
Use an array of functions.
void func1(){ std::cout << "1" << std::endl; }
void func2(){ std::cout << "2" << std::endl; }
void func3(){ std::cout << "3" << std::endl; }
typedef void (* func ) () ;
class Handler {
public:
func handleMessage(int code)const{
static const func F[] = { func1, func2, func3 };
return F[ code ];
}
};
int main()
{
Handler handler;
func f = handler.handleMessage(0); // returns func1
f();
}
live example
you can map the ints to a function or lambda, but read befor what at() does and what happens if the key is not found!!
void function1()
{
std::cout << "q1" << std::endl;
}
void function2()
{
std::cout << "q2" << std::endl;
}
int main(int argc, char* argv[])
{
std::map<int, std::function<void(void)>> map;
map.insert(std::make_pair(1, function1));
map.insert(std::make_pair(1, function2));
map.at(1)();
I would like to offer solution without any if-else block. You just need to templatize your Handler::handleMessage function. Something like this:
// Class declaration
class Handler
{
private:
public:
template<int code>
void handleMessage();
};
and specialize the function template for particular codes:
// Function template specializations.
template<>
void Handler::handleMessage<1>()
{
std::cout << "1" << std::endl;
}
template<>
void Handler::handleMessage<2>()
{
std::cout << "2" << std::endl;;
}
template<>
void Handler::handleMessage<3>()
{
std::cout << "3" << std::endl;;
}
// All cases, except 1, 2 and 3
template<int code>
void Handler::handleMessage()
{
std::cout << "Anything else" << std::endl;;
}
The usage may look like:
Handler h;
h.handleMessage<1>(); // Prints 1
h.handleMessage<2>(); // Prints 2
h.handleMessage<3>(); // Prints 3
h.handleMessage<323>(); // Prints 'Anything else'
I've just found something as get the name of current function or get name of the caller, some variants to get or call the name as string.
What I want is to get the name of the function that I passed in arguments. Like this:
void Bar()
{
//do something
}
void Foo(void (*f)())
{
//this will output: Foo
std::cout << __FUNCTION__ << std::endl;
//How do I get the name passed to f? (in this case: Bar)
}
int main()
{
Foo(Bar);
return 0;
}
Thanks.
Edit: Here is an extremely near code for what I'm trying following the suggestion of #Jose.
thread_local const char * m_last_function_called = "";
#define register_function() {m_last_function_called = __FUNCTION__;}
inline const char * get_last_function() { return m_last_function_called; }
// const char * g_last_function_called = "";
// #define register_function() { g_last_function_called = __FUNCTION__; }
// inline const char * get_last_function() { return g_last_function_called; }
void print_last_func()
{
static std::mutex sync_cout;
std::lock_guard<std::mutex> l(sync_cout);
std::cout << get_last_function() << std::endl;
}
bool interruptFooTimer = false;
int FooTimer()
{
register_function();
print_last_func();
std::cout << "FooTimer.start" << std::endl;
int t = 0;
while(t<10)
{
if(interruptFooTimer==false)
{
sleep(1);
t++;
std::cout << "\tt=" << t << std::endl;
}
else
{
return -1;
}
}
std::cout << "FooTimer.end" << std::endl;
return 0;
}
void CallTrd(int (*f)())
{
std::thread TrdTemp(f);
TrdTemp.detach();
TrdTemp.~thread();
print_last_func();
}
int main()
{
CallTrd(FooTimer);
print_last_func();
int c = 0;
while(c<15)
{
if(c==7) {interruptFooTimer=true;}
sleep(1);
c++;
std::cout << "c=" << c << std::endl;
print_last_func();
}
return 0;
}
Observe that I call print_last_func() in different moments and all get the same value that was initialized in the variable. This sample code calls a thread without using join() because I can't wait for the thread to finish and also implement the detach() and ~thread to finish my program without any exception. The interruptFooTimer I'm using to safely "terminate" my thread.
What am I missing to get global the value acquired in register_function??
You cannot. __FUNCTION__ is expanded by compiler during the compilation time. You cannot get this information in runtime.
Use a helper macro:
#define Foo(x) FooHelper(#x, x)
void FooHelper(const char *f_name, void (*f)())
{
cout << f_name;
}
As others have already noted, you cannot do this directly.
First of all I would use std::function instead of a raw function pointer and std::string to hold the function's name.
I would also wrap these up like this:
template<class T>
struct Functor
{
std::function<T> functor_;
std::string name_;
Functor(const std::function<T>& functor, const std::string& name)
: functor_(functor)
, name_(name)
{}
};
You can then use it like so:
void Bar()
{
// do something
}
void Foo(const Functor<void()>& functor)
{
}
int main()
{
Functor<void()> f(std::bind(Bar), "Bar");
Foo(f);
return 0;
}
You can also use a macro to make things easier for you.
#define FUNCTOR(t, x, ...) Functor<t>(std::bind(&x, __VA_ARGS__), #x)
Which can be used like this:
int main()
{
auto f = FUNCTOR(void(), Bar);
return 0;
}
Note that if you take this approach that the function name might not be the same as what using __FUNCTION__ yields.
I don't know why you wanna do it, but I hope it's for debugging purpose. Obviously the easiest path is to pass the __FUNCTION__ as an argument as pointed out. But in my point of view, there's a better approach.
You could have for example, a global variable:
thread_local const char * g_last_function_called = "";
#define register_function() { g_last_function_called = __FUNCTION__; }
inline const char * get_last_function() { return g_last_function_called; }
void Bar()
{
register_function()
}
void Foo(void (*f)())
{
std::cout << get_last_function() << std::endl;
}
void my_func_that_doesnt_accept_nulls(CClass * p)
{
if (p == nullptr)
{
std::cout << " function" << get_last_function();
std::cout << "passed a bad pointer, fix it" << std::endl;
return;
}
}
Of course, this will not give you the right result in multi-thread, but you can probably fix that using a thread_local const char * g_last_function_called = "" variable.
The reason why I like this method is because all you have to do to remove it from your project is just a simple "find and replace" of register_function(), and since it uses plain pointers, there's no way it can slow your program at all.
Edit: this is how I'm testing the code
void print_last_func()
{
static std::mutex sync_cout;
std::lock_guard<std::mutex> l(sync_cout);
std::cout << get_last_function() << std::endl;
}
void HelloWorld1()
{
register_function();
print_last_func();
}
void HelloWorld2()
{
register_function();
print_last_func();
}
int main()
{
register_function();
std::thread t1(HelloWorld1);
std::thread t2(HelloWorld2);
print_last_func();
t1.join();
t2.join();
return 0;
}
I get: HelloWorld1, HelloWorld2, and main