I'm using Visual Studio 2017 and came across a bug which I cannot understand. I have this code as an example:
#include <iostream>
struct Foo
{
int a = 0;
int c;
int *b;
Foo(int n)
{
a = n;
b = &c;
}
};
struct Bar
{
Foo nestedFoo = Foo(0);
Bar(int n)
{
std::cout << "bar constructor nestedFoo before:" << std::endl;
std::cout << &nestedFoo.a << std::endl;
std::cout << nestedFoo.b << std::endl;
nestedFoo = Foo(n);
std::cout << "bar constructor nestedFoo after:" << std::endl;
std::cout << &nestedFoo.a << std::endl;
std::cout << nestedFoo.b << std::endl;
}
};
struct App
{
Bar bar = Bar(2);
};
int main()
{
App app;
Bar bar = Bar(10);
Foo foo = Foo(5);
std::cout << "foo before:" << std::endl;
std::cout << &foo.a << std::endl;
std::cout << foo.b << std::endl;
foo = Foo(20);
std::cout << "foo after:" << std::endl;
std::cout << &foo.a << std::endl;
std::cout << foo.b << std::endl;
std::cout << "bar.nestedFoo:" << std::endl;
std::cout << &bar.nestedFoo.a << std::endl;
std::cout << bar.nestedFoo.b << std::endl;
std::cout << "app.bar.nestedFoo:" << std::endl;
std::cout << &app.bar.nestedFoo.a << std::endl;
std::cout << app.bar.nestedFoo.b << std::endl;
return 0;
}
I expected the pointer and the address of the member to be the same after the reassignment but it seems this is not the case, &Foo.a and Foo.b have different values:
bar constructor nestedFoo before:
0058FEE8
0058FEE8
bar constructor nestedFoo after:
0058FEE8
0058FC24
bar constructor nestedFoo before:
0058FED8
0058FED8
bar constructor nestedFoo after:
0058FED8
0058FD04
foo before:
0058FEC8
0058FEC8
foo after:
0058FEC8
0058FDF8
bar.nestedFoo:
0058FED8
0058FD04
app.bar.nestedFoo:
0058FEE8
0058FC24
At first I thought it only happened when I had Foo as a member of another struct, but it seems to happen even at a top-level assignment. So what am I doing wrong?
Any help will be appreciated, thank you!
Edit: showing that assigning b to &c which is a member that doesn't get (explicitly) reassigned also doesn't work.
Related
I wrote a generic class for handling and executing a function pointer. This is a simplified equivalent of std::function and std::bind. To handle member functions I use cast to internal EventHandler::Class type. Question: is it ok to cast it that way? Will it work in all cases when invoking handled function?
template <typename ReturnType, typename... Arguments>
class EventHandler
{
class Class {};
ReturnType (Class::*memberFunction)(Arguments...) = nullptr;
union {
Class *owner;
ReturnType(*function)(Arguments...) = nullptr;
};
public:
EventHandler() = default;
EventHandler(EventHandler &&) = default;
EventHandler(const EventHandler &) = default;
EventHandler &operator=(EventHandler &&) = default;
EventHandler &operator=(const EventHandler &) = default;
EventHandler(ReturnType (*function)(Arguments...)) :
function(function)
{
}
template <typename Owner>
EventHandler(Owner *owner, ReturnType (Owner::*memberFunction)(Arguments...)) :
memberFunction((ReturnType (Class::*)(Arguments...)) memberFunction),
owner((Class *) owner)
{
}
template <typename Owner>
EventHandler(const Owner *owner, ReturnType (Owner::*memberFunction)(Arguments...) const) :
memberFunction((ReturnType (Class::*)(Arguments...)) memberFunction),
owner((Class *) owner)
{
}
ReturnType operator()(Arguments... arguments)
{
return memberFunction ?
(owner ? (owner->*memberFunction)(arguments...) : ReturnType()) :
(function ? function(arguments...) : ReturnType());
}
};
The implementation provides handle for a global function, a member function and a const member function. Obviously there is volatile and const volatile that is not show here for clarity.
EDIT
All the code below is just a representation of all of kinds of supported functions.
class Object
{
public:
double y = 1000;
Object() = default;
Object(double y) : y(y) {}
static void s1(void) { std::cout << "s1()" << std::endl; }
static void s2(int a) { std::cout << "s2(a:" << 10 + a << ")" << std::endl; }
static void s3(int a, float b) { std::cout << "s3(a:" << 10 + a << ", b:" << 10 + b << ")" << std::endl; }
static int s4(void) { std::cout << "s4(): "; return 10 + 4; }
static Object s5(int a) { std::cout << "s5(a:" << 10 + a << "): "; return Object(10 + 5.1); }
static float s6(int a, Object b) { std::cout << "s6(a:" << 10 + a << ", b:" << 10 + b.y << "); "; return 10 + 6.2f; }
void m1(void) { std::cout << "m1()" << std::endl; }
void m2(int a) { std::cout << "m2(a:" << y + a << ")" << std::endl; }
void m3(int a, float b) { std::cout << "m3(a:" << y + a << ", b:" << y + b << ")" << std::endl; }
int m4(void) { std::cout << "m4(): "; return ((int) y) + 4; }
Object m5(int a) { std::cout << "m5(a:" << y + a << "): "; return Object(y + 5.1); }
float m6(int a, Object b) { std::cout << "m6(a:" << y + a << ", b:" << y + b.y << "); "; return ((int) y) + 6.2f; }
void c1(void) const { std::cout << "c1()" << std::endl; }
void c2(int a) const { std::cout << "c2(a:" << y + a << ")" << std::endl; }
void c3(int a, float b) const { std::cout << "c3(a:" << y + a << ", b:" << y + b << ")" << std::endl; }
int c4(void) const { std::cout << "c4(): "; return ((int) y) + 4; }
Object c5(int a) const { std::cout << "c5(a:" << y + a << "): "; return Object(y + 5.1); }
float c6(int a, Object b) const { std::cout << "c6(a:" << y + a << ", b:" << y + b.y << "); "; return ((int) y) + 6.2f; }
};
void f1(void) { std::cout << "f1()" << std::endl; }
void f2(int a) { std::cout << "f2(a:" << a << ")" << std::endl; }
void f3(int a, float b) { std::cout << "f3(a:" << a << ", b:" << b << ")" << std::endl; }
int f4(void) { std::cout << "f4(): "; return 4; }
Object f5(int a) { std::cout << "f5(a:" << a << "): "; return Object(5.1); }
float f6(int a, Object b) { std::cout << "f6(a:" << a << ", b:" << b.y << "); "; return 6.2f; }
Here is the usage example for all of the above functions
int main()
{
std::cout << "=== Global functions" << std::endl;
EventHandler ef1(f1); ef1();
EventHandler ef2(f2); ef2(2);
EventHandler ef3(f3); ef3(3, 3.1f);
EventHandler ef4(f4); std::cout << ef4() << std::endl;
EventHandler ef5(f5); std::cout << ef5(5).y << std::endl;
EventHandler ef6(f6); std::cout << ef6(6, Object(6.1)) << std::endl;
std::cout << std::endl;
std::cout << "=== Member static functions" << std::endl;
EventHandler es1(Object::s1); es1();
EventHandler es2(Object::s2); es2(2);
EventHandler es3(Object::s3); es3(3, 3.1f);
EventHandler es4(Object::s4); std::cout << es4() << std::endl;
EventHandler es5(Object::s5); std::cout << es5(5).y << std::endl;
EventHandler es6(Object::s6); std::cout << es6(6, Object(6.1)) << std::endl;
std::cout << std::endl;
std::cout << "=== Member functions" << std::endl;
Object object(20);
EventHandler em1(&object, &Object::m1); em1();
EventHandler em2(&object, &Object::m2); em2(2);
EventHandler em3(&object, &Object::m3); em3(3, 3.1f);
EventHandler em4(&object, &Object::m4); std::cout << em4() << std::endl;
EventHandler em5(&object, &Object::m5); std::cout << em5(5).y << std::endl;
EventHandler em6(&object, &Object::m6); std::cout << em6(6, Object(6.1)) << std::endl;
std::cout << std::endl;
std::cout << "=== Member const functions" << std::endl;
const Object constObject(30);
EventHandler ec1(&constObject, &Object::c1); ec1();
EventHandler ec2(&constObject, &Object::c2); ec2(2);
EventHandler ec3(&constObject, &Object::c3); ec3(3, 3.1f);
EventHandler ec4(&constObject, &Object::c4); std::cout << ec4() << std::endl;
EventHandler ec5(&constObject, &Object::c5); std::cout << ec5(5).y << std::endl;
EventHandler ec6(&constObject, &Object::c6); std::cout << ec6(6, Object(6.1)) << std::endl;
system("pause");
return 0;
}
Finally - to the point - here an example that shows how much easier in use is the EventHandler I prepared when compared to std::function interface. And actually the reason of such approach.
EventHandler<float, int, Object> example;
example = f6;
example(7, Object(7.1));
example = EventHandler(&object, &Object::m6);;
example(8, Object(8.1));
It’s undefined behavior to call a function through a function pointer(-to-member) of a different type. (Some practical reasons for this rule are that the object’s address might need to be adjusted to call a member function of a base class or that a vtable might be involved.) You can use type erasure to allow calling member functions on objects of different types (which is what std::bind does), or you can (restrict to member functions and) add the class type as a template parameter.
Of course, the usual answer is to just use std::function with a lambda that captures the object in question and calls whatever member function. You can also take the C approach and define various functions with a void* parameter that cast that parameter to a known class type and call the desired member function.
is there any mechanism with elegant API to handle functions of any type?
I mean a class that automagically detects type of a function (its return type, arguments, if it is a class member, a const etc), something that I could easily use to handle any kind of events, like in the example below:
class Abc
{
public:
void aFunc() { std::cout << "a()" << std::endl; }
void cFunc(int x, char y) { std::cout << "c(" << x << ", " << y << ")" << std::endl; }
};
void bFunc(int x) { std::cout << "b(" << x << ")" << std::endl; }
int main()
{
Abc abc;
EventHandler a = abc.aFunc;
EventHandler b = bFunc;
EventHandler c = abc::cFunc;
a();
b(123);
c(456789, 'f');
std::cout << "Done." << std::endl;
return 0;
}
The std::function and std::bind can be used internally, but the bind should be done automatically.
I'm working on something which lead me to a situation similar to the example below. I want b to point to a after on test(). Is it possible to this or creating a copy is the only solution?
Thanks.
void test(Object* a, Object* b)
{
std::cout << "test a: " << a << std::endl;
std::cout << "test b: " << b << std::endl;
b = a;
std::cout << "test b assigned: " << b << std::endl;
}
int main()
{
Object* a = new Object();
Object* b = nullptr;
test(a, b);
std::cout << "a: " << a << std::endl;
std::cout << "b: " << b << std::endl;
delete a;
delete b;
}
test a: 0x1aa8930
test b: 0
test b assigned: 0x1aa8930
a: 0x1aa8930
b: 0
In order to modify a pointer, you need to either:
Pass a pointer to your pointer
void test(Object **a, Object **b)
test(&a,&b);
After which you need to dereference that if you want to change the pointer.
Pass a reference to your pointer
void test(Object *&a, Object *&b)
test(a,b);
Here you won't have to dereference anything if you want to change the address.
For a, you don't necessarily have to pass a pointer to pointer or a reference to pointer (unless you plan to modify it too in the future), but for b you have to in order to modify it.
P.S. change your void main() to int main()
You can do like this:
void test(Object* a, Object*& b)
{
std::cout << "test a: " << a << std::endl;
std::cout << "test b: " << b << std::endl;
b = a;
std::cout << "test b assigned: " << b << std::endl;
}
int main()
{
Object* a = new Object();
Object* b = NULL;
test(a, b);
std::cout << "a: " << a << std::endl;
std::cout << "b: " << b << std::endl;
delete a, b;
}
Output will be:
test a: 0x7a0d28
test b: 0
test b assigned: 0x7a0d28
a: 0x7a0d28
b: 0x7a0d28
Its because in question, you have passed the value of b, not a reference to it, which is required to modify it. Variables can only be modified by a function if that function receives the address of those variables, and that can only can be done via either a pointer (Object**) or a reference (Object* &)
Nowadays we have something called shared_ptr, which does exacly what you want. Plus you don't have to clean it up after you're finished with it!
Example:
#include <memory>
#include <iostream>
using Object = int;
void test(const std::shared_ptr<Object>& a, std::shared_ptr<Object>& b)
{
b = a;
std::cout << "test b assigned: " << b.get() << "\n";
}
int main()
{
auto a = std::make_shared<Object>();
std::shared_ptr<Object> b;
std::cout << "test a: " << a.get() << "\n";
std::cout << "test b: " << b.get() << "\n";
test(a, b);
std::cout << "test a: " << a.get() << "\n";
std::cout << "test b: " << b.get() << "\n";
return 0;
}
output (g++ 5.4.0):
test a: 0x21e2c30
test b: 0
test b assigned: 0x21e2c30
test a: 0x21e2c30
test b: 0x21e2c30
live demo
#include <iostream>
#include <memory>
int main () {
std::shared_ptr<int> foo;
std::shared_ptr<int> bar (new int(10));
foo = bar;
bar.reset(new int(20));
std::cout << "*foo: " << *foo << '\n';
std::cout << "*bar: " << *bar << '\n';
return 0;
}
output:
*foo: 10
*bar: 20
#include <iostream>
#include <memory>
int main () {
int * foo;
int *bar = new int(10);
foo = bar;
*bar = 20;
std::cout << "*foo: " << *foo << '\n';
std::cout << "*bar: " << *bar << '\n';
return 0;
}
output:
*foo: 20
*bar: 20
How to make shared_pt B from shared_pt A that B has the same inner value as A whatever A change later (like the above raw pointer example) ?
They behave the same way if you do the same thing
int main() {
std::shared_ptr<int> foo;
std::shared_ptr<int> bar(new int(10));
foo = bar;
*bar = 20;
std::cout << "*foo: " << *foo << '\n';
std::cout << "*bar: " << *bar << '\n';
std::cin.get();
return 0;
}
Does destructor get called on std::map elements when std::map::clear is used?
I tried to debug for std::map<string,string> but could not see std::string destructor getting invoked. Can any one please help my understanding?
Documentation states it gets called, but I could not notice it.
Documentation is right, it does get called.
The destruction will be done by the method std::allocator<T>::deallocate(). Trace through that in your debugger.
http://www.cplusplus.com/reference/std/memory/allocator/
The destructor does get called. Here is an example to illustrate:
#include <iostream>
#include <map>
class A
{
public:
A() { std::cout << "Constructor " << this << std::endl; }
A(const A& other) { std::cout << "Copy Constructor " << this << std::endl; }
~A() { std::cout << "Destructor " << this <<std::endl; }
};
int main()
{
std::map<std::string, A> mp;
A a;
mp.insert(std::pair<std::string, A>("hello", a));
mp.clear();
std::cout << "Ending" << std::endl;
}
This will report an output similar to this:
Constructor 0xbf8ba47a
Copy Constructor 0xbf8ba484
Copy Constructor 0xbf8ba48c
Copy Constructor 0x950f034
Destructor 0xbf8ba48c
Destructor 0xbf8ba484
Destructor 0x950f034
Ending
Destructor 0xbf8ba47a
So, you can see that the destructors get called by the calling the clear function.
try with a std::map<A,B> where A and B are custom types that have a destructor in which you have set a breakpoint. You will see that it does get invoked, and exactly what scope this destruction happens at.
Here is a bit more of a complete test, building on Chris Mansley's code as I wanted to see the effect on values, ptrs and refs - and I wanted to see the difference
between clear and erase. No difference. In summary the destructor is only called
for value types, which you would expect. I just like to check my understanding 8)
#include <iostream>
#include <map>
class A
{
public:
std::string some_data;
A(std::string some_data) : some_data(some_data) {
std::cout << " A(" << some_data << ") #" << this << std::endl;
}
A(const A& other) {
some_data = other.some_data;
std::cout << " Copy A(" << other.some_data << ") #" << this << std::endl;
}
~A() {
std::cout << " Destruct ~A(" << some_data << ") #" << this << std::endl;
}
};
void clear_test_value (void)
{
std::cout << "clear_test_value() {" << std::endl;
std::map<std::string, A> mp;
A a("A1 data");
mp.insert(std::pair<std::string, A>("key1", a));
mp.clear();
std::cout << "}" << std::endl;
std::cout << std::endl;
}
void erase_test_value (void)
{
std::cout << "erase_test_value() {" << std::endl;
std::map<std::string, A> mp;
A a("A1 data");
mp.insert(std::pair<std::string, A>("key2", a));
auto f = mp.find("key2");
if (f == mp.end()) {
std::cout << "failed to find element {" << std::endl;
return;
}
mp.erase(f);
std::cout << "}" << std::endl;
std::cout << std::endl;
}
void clear_test_ptr (void)
{
std::cout << "clear_test_ptr() {" << std::endl;
std::map<std::string, A*> mp;
A a("A1 data");
mp.insert(std::pair<std::string, A*>("key1", &a));
mp.clear();
std::cout << "}" << std::endl;
std::cout << std::endl;
}
void erase_test_ptr (void)
{
std::cout << "erase_test() {" << std::endl;
std::map<std::string, A*> mp;
A a("A1 data");
mp.insert(std::pair<std::string, A*>("key2", &a));
auto f = mp.find("key2");
if (f == mp.end()) {
std::cout << "failed to find element {" << std::endl;
return;
}
mp.erase(f);
std::cout << "}" << std::endl;
std::cout << std::endl;
}
void clear_test_ref (void)
{
std::cout << "clear_test_ref() {" << std::endl;
std::map<std::string, A&> mp;
A a("A1 data");
mp.insert(std::pair<std::string, A&>("key1", a));
mp.clear();
std::cout << "}" << std::endl;
std::cout << std::endl;
}
void erase_test_ref (void)
{
std::cout << "erase_test_ref() {" << std::endl;
std::map<std::string, A&> mp;
A a("A1 data");
mp.insert(std::pair<std::string, A&>("key2", a));
auto f = mp.find("key2");
if (f == mp.end()) {
std::cout << "failed to find element {" << std::endl;
return;
}
mp.erase(f);
std::cout << "}" << std::endl;
std::cout << std::endl;
}
int main ()
{
clear_test_value();
erase_test_value();
clear_test_ptr();
erase_test_ptr();
clear_test_ref();
erase_test_ref();
return (0);
}
Output:
clear_test_value() {
A(A1 data) #0x7ffee07389a0
Copy A(A1 data) #0x7ffee0738960
Copy A(A1 data) #0x7fe98fc029c8
Destruct ~A(A1 data) #0x7ffee0738960
Destruct ~A(A1 data) #0x7fe98fc029c8
}
Destruct ~A(A1 data) #0x7ffee07389a0
erase_test_value() {
A(A1 data) #0x7ffee07387f0
Copy A(A1 data) #0x7ffee07387b0
Copy A(A1 data) #0x7fe98fc029c8
Destruct ~A(A1 data) #0x7ffee07387b0
Destruct ~A(A1 data) #0x7fe98fc029c8
}
Destruct ~A(A1 data) #0x7ffee07387f0
clear_test_ptr() {
A(A1 data) #0x7ffee07389b0
}
Destruct ~A(A1 data) #0x7ffee07389b0
erase_test() {
A(A1 data) #0x7ffee0738800
}
Destruct ~A(A1 data) #0x7ffee0738800
clear_test_ref() {
A(A1 data) #0x7ffee07389b0
}
Destruct ~A(A1 data) #0x7ffee07389b0
erase_test_ref() {
A(A1 data) #0x7ffee0738800
}
Destruct ~A(A1 data) #0x7ffee0738800