What are Body named requirements of functional nodes in OneTBB.FlowGraph? - c++

It's not clear to me what are the Body named requirements of functional nodes. Does it need to be CopyContructible, MoveContructible? Obviously Callable.
A simple program shows copies of the Body, apparently unrelated to the concurrency number:
#include <iostream>
#include <tbb/flow_graph.h>
using namespace tbb::flow;
struct function_body
{
function_body() { std::cout << "body constructed" << std::endl; }
~function_body() { std::cout << "body destructed" << std::endl; }
function_body(function_body const&) { std::cout << "body copied" << std::endl; }
function_body(function_body&&) { std::cout << "body moved" << std::endl; }
int operator()(int rhs) const { return rhs + 1; }
};
int main()
{
const int max_iteration = 10000;
const int concurrency = 5;
graph g;
auto src = input_node<int>(g, [counter = 0, max_iteration](tbb::flow_control& fc) mutable {
if (counter < max_iteration) {
fc.stop();
return counter;
}
return ++counter;
});
auto fun = function_node<int, int>(g, concurrency, function_body{});
make_edge(src, fun);
src.activate();
g.wait_for_all();
return 0;
}
results in
body constructed
body copied
body copied
body destructed
body destructed
body destructed
In which context do these copies happen? Does spawning a task involve copying the body?
Most of the examples I have looked at defines lambda body that captures resources by reference, so shared between body instances and cheap to copy.

Related

How can a simple std::cout << "---" << std::endl; alter how UB manifests?

Some background
Today I saw, in the body of a function kaboom, a local shared_ptr pointing to a *global object and, being captured by reference and its pointee returned by reference by a lambda, i.e. [&local]() -> auto& { return *local; }; this lambda was stored somehow for further use after the function kaboom returned.
As soon as I saw this, I thought the code was invoking undefined behavior. I tried to make a minimal example to support my claim, and came up with the following.
#include <iostream>
#include <memory>
#include <functional>
#include <vector>
struct Resource {
const int i{3};
};
std::shared_ptr<Resource> global = std::make_unique<Resource>();
auto getSharedStuff() {
return global;
}
std::vector<std::function<Resource&()>> actions{};
void kaboom() {
std::shared_ptr<Resource> local = getSharedStuff();
actions.push_back([&local]() -> auto& { return *local; });
}
int main() {
kaboom();
std::cout << global->i << std::endl;
std::cout << actions[0]().i << std::endl;
}
The simple fact that the 2 couts give 3 and 0 respectively is proof for me that I'm observing UB (3 is correct, and 0 could have been anything, including 3, but I've been lucky that it was not 3, so the UB is well manifest).
Good.
The code I'm curious about
But before getting there, an intermediate version of the repro above was this:
#include <iostream>
#include <memory>
#include <functional>
#include <vector>
struct Resource {
Resource(Resource const&) = delete;
Resource(Resource&&) = delete;
Resource() { std::cout << this << "'s ctor" << std::endl; }
~Resource() { std::cout << this << "'s dtor" << std::endl; }
void operator()() { std::cout << this << "'s operator()" << std::endl; }
};
std::shared_ptr<Resource> global = std::make_unique<Resource>();
auto getSharedStuff() {
return global;
}
std::vector<std::function<Resource&()>> actions{};
void kaboom() {
std::shared_ptr<Resource> local = getSharedStuff();
actions.push_back([&local]() -> auto& { return *local; });
}
int main() {
kaboom();
std::cout << "---" << std::endl;
actions[0]()();
}
which can result in this output
0xc602b0's ctor
---
0x7f0f48f7f4a0's operator()
0xc602b0's dtor
I'm kind of ok with this, as actions[0]()() is dereferencing a destroyed shared_ptr and calling operator() on the screwed up result, so I accept that this can be screwup too.
What's funny, though, is that removing std::cout << "---" << std::endl; makes UB less manifest, as the output becomes something like this:
0xce92b0's ctor
0xce92b0's operator()
0xce92b0's dtor
So my question is: how can a "simple" line as std::cout << "---" << std::endl; affect this way the manifestation of UB?

Access map element via pointer in C++

I switched from c to c++ recently and just can't figure out what I'm doing wrong here.
I would like to access and set the member of a map via another function.
Here is my example which you can just copy to cpp.sh or so if you like
#include <iostream>
#include <map>
using namespace std;
struct test{
int i;
int j;
};
void addValues(test* val){
if (val == NULL){
val = new test();
cout<<"new";
}
val->i = 10;
val->j = 12;
}
void printVal(test* val){
cout<<"finish " << val->i << " " << val->j;
}
int main()
{
map<string, test*> bla = {{"test1",NULL}};
addValues(bla.at("test1"));
printVal(bla.at("test1"));
return 0;
}
code from my project is a little bit more complex but it's basically this problem. I created a test in addValues() and have not deleted it. Why am I not able to print this value in printVal()? What am I missing?
Thanks in advance!
Parameters are passed by value. Pointers are no exception to that. Your addValues modifies a local copy of the pointer when a nullptr is passed. Modifying that local copy does not affect the pointer in the map. Pass the pointer by reference:
void addValues(test*& val){
if (val == nullptr){
val = new test();
cout<<"new";
}
val->i = 10;
val->j = 12;
}
Or better yet, do not use raw pointers in the first place. Moreover, consider to write a constructor that initializes the members of test instead of relying on the caller to initialize them.
Example :
#include <iostream>
#include <map>
//using namespace std; NO teach yourself not to do this.
struct test
{
int i = 0; // <== in c++ you can initialize values of structs
int j = 0;
};
// this instead of printVal
std::ostream& operator<<(std::ostream& os, const test& t)
{
os << "i = " << t.i << ", j = " << t.j << "\n";
return os;
}
int main()
{
std::map<std::string, test> map =
{
{"test1",{1,1}},
{"test2",{2,2}},
};
// loop over all entries in the map
// range based for loop.
// each entry in the map is a key,value pair (not they key, not the value but a pair)
// https://en.cppreference.com/w/cpp/language/range-for
std::cout << "range based for over keyvalue pairs\n";
for (const auto& kv : map)
{
// note kv.second is where we use operator<< from earlier.
std::cout << "Key : " << kv.first << ", value : " << kv.second << "\n";
}
std::cout << "\n";
// structured bindings make code more readable
// https://en.cppreference.com/w/cpp/language/structured_binding
std::cout << "range based for using structured bindings \n";
for (const auto& [key, value] : map)
{
std::cout << "Key : " << key << ", value : " << value <<"\n";
}
std::cout << "\n";
return 0;
}

Avoid copy construction by std::transform

I'm calling std::transform with a lambda that takes by reference and gives back a reference to the vector element. However, according to my program output, the copy constructor is called and the objects are NOT the same.
Code:
#include <algorithm>
#include <iostream>
#include <vector>
class Math
{
private:
int val_ = 5;
public:
Math(const Math& m) {
std::cout << "Copy constructor, our address: " << this << ", his address: " << &m << std::endl;
}
Math(int val) : val_(val) {
std::cout << "Object constructed with " << val << std::endl;
}
};
int main()
{
std::vector<Math> v_math = { { 5 }, { 10 } };
std::transform(
begin(v_math),
end(v_math),
begin(v_math),
[](const Math& m)-> const Math& {
return m;
});
}
Output (Godbolt):
Object constructed with 5
Object constructed with 10
Copy constructor, our address: 0x23d7ec0, his address: 0x7fff9dc499a8
Copy constructor, our address: 0x23d7ec4, his address: 0x7fff9dc499ac
So three things are unclear to me right now:
Why are the objects different? Shouldn't they be the same?
Why is one object's address bigger than the other? Is this because the copied-to object remains on the stack which has offset-pointers?
How can I avoid copy construction as well (actually I just "misuse" std::transform for a declarative way of invoking a lambda on every std::vector element)?
The copies have nothing to do with your usage of std::transform. They happen when you construct your v_math std::vector, because you're using a std::initializer_list constructor, which forces copies during construction.
In your std::transform call, operator=(const Math&) is called, change your code to the following to see this.
class Math
{
private:
int val_ = 5;
public:
Math(const Math& m) {
std::cout << "Copy constructor, our address: " << this << ", his address: " << &m << std::endl;
}
Math(int val) : val_(val) {
std::cout << "Object constructed with " << val << std::endl;
}
Math& operator=(const Math& other) {
val_ = other.val_;
std::cout << "Operator=(const Math&) called!\n";
return *this;
}
};
int main()
{
std::vector<Math> v_math = { { 5 }, { 10 } };
std::cout << "After constructing v_math!\n";
std::transform(
begin(v_math),
end(v_math),
begin(v_math),
[](const Math& m)-> const Math& {
return m;
});
std::cout << "After std::transform call!\n";
}

How to prevent compilation of passed lambda, if arguments are not references

In one of my projects I'm using a small utility function, which takes a Message struct and a lambda function, that modifies this message struct.
Now, I unintentionally passed a lambda without the necessary reference &. It perfectly compiles, but doesn't gave the desired output.
As for me, there should be one of the two following behaviors:
Forgetting to write auto&, but just auto should lead to compilation errors
Writing just auto should be interpreted as auto&.
It is possible to prevent compilation in case of a missing & or even better to interpret auto as auto& automatically?
#include <iostream>
#include <functional>
#include <boost/variant.hpp>
struct Message {
int x;
int y;
};
void changeMessage(Message& m, const std::function<void(Message&)>& messageModifier) {
std::cout << "Message before:" << m.x << " " << m.y << "\n";
messageModifier(m);
std::cout << "Message after:" << m.x << " " << m.y << "\n";
}
int main(int, char**) {
{
std::function<void(int&)> f = [](int&) {};
std::function<void(int)> g = [](int) {};
f = g; // This compiles.
}
{
std::function<void(int&)> f = [](int&) {};
std::function<void(int)> g = [](int) {};
//g = f; // This does not compile. Makes perfect sense.
}
Message m{ 10,20 };
{
changeMessage(m, [](auto m) { m.x++; m.y--; }); // User unintentionally forgot &! Can I prevent this from compilation?
std::cout << "Message outside: " << m.x << " " << m.y << "\n";
}
{
changeMessage(m, [](auto& m) { m.x++; m.y--; });
std::cout << "Message outside: " << m.x << " " << m.y << "\n";
}
}
One way to prevent passing Message by value (and auto itself is never a reference) is to disable copy construction:
struct Message {
Message() = default;
Message(const Message&) = delete;
int x;
int y;
};
Another solution suggested by #L. F. is to check that lambda doesn't accept rvalues:
template<class Fn>
void change_message(Message& m, Fn fn) {
static_assert(!std::is_invocable_v<Fn, Message&&>);
fn(m);
}

Cocos2dx Style and Implementation

I'm sort of getting back into cocos2dx development. This time I would like to fully understand the subtleties of the library. I have begun to get a good understanding of singleton classes, which they use throughout. I do have a question about their implementation. Here is a basic example of a singleton class being used
class GlobalClass
{
int m_value;
public:
GlobalClass(int v = 0)
{
m_value = v;
}
int get_value()
{
return m_value;
}
void set_value(int v)
{
m_value = v;
}
};
// Default initialization
GlobalClass *global_ptr = 0;
void foo(void)
{
// Initialization on first use
if (!global_ptr)
global_ptr = new GlobalClass;
global_ptr->set_value(1);
cout << "foo: global_ptr is " << global_ptr->get_value() << '\n';
}
void bar(void)
{
if (!global_ptr)
global_ptr = new GlobalClass;
global_ptr->set_value(2);
cout << "bar: global_ptr is " << global_ptr->get_value() << '\n';
}
int main()
{
if (!global_ptr)
global_ptr = new GlobalClass;
cout << "main: global_ptr is " << global_ptr->get_value() << '\n';
foo();
bar();
}
My question is about the initialization that comes after class definition and before foo, bar, and main. Essentially, I am interested in this line
GlobalClass *global_ptr = 0;
Where is the stack instance being initialized? You need some interface for that global pointer to the heap that is the member of that stack instance, right? If so, where is this done in cocos2dx?
My other question is about the use of the scope resolution operator (::) to call methods such as in
glview = GLViewImpl::create("MyView")
What purpose does this serve? Is this to overstep name spacing to access singleton instances?
If any of my understandings are wrong, especially on singletons, please correct them.
I think Syntax of your Singleton class is wrong.
It should be like this :
class GlobalClass
{
static GlobalClass* _instance; // Make private so only can access with method to avoid NPE for first time access
GlobalClass(int v = 0) // Make constructor private, you can't create object outside the class
{
m_value = v;
}
int m_value;
public:
static GlobalClass* getInstance();
int get_value()
{
return m_value;
}
void set_value(int v)
{
m_value = v;
}
};
GlobalClass* GlobalClass::_instance = 0;
GlobalClass* GlobalClass::getInstance() {
return GlobalClass::_instance == nullptr ? GlobalClass::_instance = new GlobalClass : GlobalClass::_instance;
}
void foo(void)
{
auto global_ptr = GlobalClass::getInstance();
global_ptr->set_value(1);
std::cout << "foo: global_ptr is " << global_ptr->get_value() << '\n';
}
void bar(void)
{
auto global_ptr = GlobalClass::getInstance();
global_ptr->set_value(2);
std::cout << "bar: global_ptr is " << global_ptr->get_value() << '\n';
}
int main()
{
auto global_ptr = GlobalClass::getInstance();
std::cout << "main: global_ptr is " << global_ptr->get_value() << '\n';
foo();
bar();
system("pause");
return 0;
}
And your second question :
Why we use or having static method create() almost in all class of cocos2d-x
In cocos2d-x we create object in two phase
- Create Object // No game Logic only set default_values
- Initialize Object
We're using Reference Count mechanism for memory management in cocos2d-x. what is it ?
These 2-phase constructor and auto-released reference count together into a static function: create()