-1

In certain embedded situations, memory needs to be moved with memcopy style functions (such as from external memory, or using closed API calls).

When such a C++ object needs to be moved this way, however it doesn't have a default constructor, you can't do something like this, :

class Object { 
    //local data
public:
    Object(/* not a default constructor */){}
}

//elsewhere:
Object o; //compiler will complain here...
memcpy_like_function(src_address, &o, sizeof(o));

because Object doesn't have a default constructor, and thus the compiler will complain about creating Object o.

Some notes from things that have shown up in the comments:

  • memcpy_like_function is like memcpy, it isn't actually memcpy. The src_address isn't a pointer to an address I can reach, or an int representing a pointer to an address I can reach. It is an int representing an address in a memory space I can't reach. The only way for me to access this memory space is with this function.
  • Object doesn't have a default constructor, has no virtual functions, is neither inherited from, nor inherits anything. Object is trivially copyable.

What is the correct way to deal with creating such an object in this situation, without putting it on the heap? Preferably, I would like to get a stack allocated object that will behave correctly with scope and destructors. For the purposes of this question, Object is not inheriting from anything.

trincot
  • 317,000
  • 35
  • 244
  • 286
Andrew Spott
  • 3,457
  • 8
  • 33
  • 59
  • Please comment before down-voting and voting to close.... it is incredibly rude to do otherwise. – Andrew Spott Oct 07 '16 at 05:14
  • @Eichhörnchen: because the memory location it is in is inaccessible outside of a closed API call... – Andrew Spott Oct 07 '16 at 05:16
  • @AndrewSpott The close-vote will include a reason - sometimes no additional comment is needed – M.M Oct 07 '16 at 05:17
  • is `src_address` just pointing to two ints, or is it actually an `Object` that was properly allocated and constructed? – M.M Oct 07 '16 at 05:18
  • 2
    @M.M: I'm sorry, I think close votes always need a comment. Especially considering this one is "unclear what is being asked". I can't fix it if I don't know what the problem is. – Andrew Spott Oct 07 '16 at 05:19
  • @M.M src_address is an int denoting an address in another memory space. `Object` is just an example, not meant to be necessarily representative of my situation. – Andrew Spott Oct 07 '16 at 05:20
  • 1
    You should post something that is actually representative of your situation, otherwise it is indeed unclear what you are asking. – M.M Oct 07 '16 at 05:21
  • For example, `Object o = *reinterpret_cast(src_address);` would be a solution based on what's posted so far , but no doubt you're going to come back and say that your situation is actually different for some reason – M.M Oct 07 '16 at 05:22
  • @M.M: but `Object` does not have a copy constructor. I think that won't compile either. – Violet Giraffe Oct 07 '16 at 05:23
  • See, this is the problem. In the code you've posted there is an implicitly-defined copy constructor. But you say in comments that the real code is different. Sorry but I have to back up that close vote unless you can actually post the real code you are asking about. – M.M Oct 07 '16 at 05:24
  • I would call the code in your question bad, and borderline dangerous. A dirty hack at best. Can you really not achieve your goal in proper object-oriented C++, with no memcpy_like_functions? – Violet Giraffe Oct 07 '16 at 05:25
  • @M.M. I think you haven't understood my comments, or you are not reading the question carefully enough. I mentioned that `src_address` is not necessarily a pointer, which makes `reinterpret_cast` useless. I never mentioned anything about a lack of an implicitly defined copy constructor, just a lack of a default constructor. – Andrew Spott Oct 07 '16 at 05:25
  • @AndrewSpott my suggested code works if `src_address` is an int containing a memory address. – M.M Oct 07 '16 at 05:27
  • @M.M: it was me who said that, not the OP. I may be wrong or misunderstanding something. – Violet Giraffe Oct 07 '16 at 05:27
  • @VioletGiraffe Ah. Apologies to Andrew Spott in that respect. Every class has a copy-constructor . (in some cases it is implicitly `delete`d or private, but nothing in this question suggests that) – M.M Oct 07 '16 at 05:28
  • @VioletGiraffe: No, I can't. Do you have any experience with embedded systems? I'm trying to access a memory space that is segmented from the normal memory space. All I have to access such a space is a function that behaves like memcopy – Andrew Spott Oct 07 '16 at 05:28
  • @VioletGiraffe: I would really like to be able to do this with C++ idioms... but all that is available to me is C style functions. The whole reason for this is to try and keep the API as C++ like as possible – Andrew Spott Oct 07 '16 at 05:30
  • @M.M: src_address is an int representing an address in another memory address space... not in the space I have access to. – Andrew Spott Oct 07 '16 at 05:32
  • @M.M: sorry, I corrected the above comment – Andrew Spott Oct 07 '16 at 05:37
  • You could use your copy function to copy the data to a buffer in the "normal" space, and then construct `o` normally – M.M Oct 07 '16 at 05:39
  • Question and comments are very unclear. http://stackoverflow.com/help/mcve – kfsone Oct 07 '16 at 05:41
  • @M.M: If you want to clarify what you mean, by "construct `o` normally", I would appreciate it. – Andrew Spott Oct 07 '16 at 05:42
  • `Object o(a, b);`, where you got `a` and `b` values set up by using your memcopy function – M.M Oct 07 '16 at 05:45
  • @M.M This implies the constructor exists to create the object from the values stored... this isn't necessarily true. – Andrew Spott Oct 07 '16 at 05:50
  • Objects can only be constructed using their constructor (or aggregate initialization, for aggregates) – M.M Oct 07 '16 at 05:54
  • Based on these comments it sounds like you need to make some modifications to `Object` to support creation via the memcpy-like function (along the lines of "evan"'s answer) – M.M Oct 07 '16 at 05:54
  • How does the object get to be in the external/unaccessible memory to start with? Also, are you able to change/enhance the class Object you are working with? – harmic Oct 07 '16 at 06:25
  • @harmic through a similar memcpy like function. At the moment I'm trying to avoid changing the Object class, though it is looking like that isn't possible. – Andrew Spott Oct 07 '16 at 06:31
  • @AndrewSpott: I see. I have embedded experience, but not with Harvard architecture. Fair enough. – Violet Giraffe Oct 07 '16 at 09:04
  • @VioletGiraffe: this actually isn't a Harvard architecture system. It is a [esp8266](https://en.wikipedia.org/wiki/ESP8266). The RTC has its own memory and offers APIs to access said memory, which is what brought up the question. – Andrew Spott Oct 07 '16 at 20:14

4 Answers4

4

This seems like a horribly bad idea, but assuming that your memcpy_like_function actually works, then you can just add a constructor to Object

class Object { 
    //local data
public:
    Object(void* src_address)
    {
        memcpy_like_function(src_address, this, sizeof(*this));
    }
};

//elsewhere:
Object o(src_address);
evan
  • 1,463
  • 1
  • 10
  • 13
2

You cannot safely copy an object using a memcpy-like function unless the object is a POD type. If the object is a POD type, you should be able to use:

char destination[sizeof(Object)];
memcpy_like_function(src_address, destination, sizeof(destination));
Object* ptr = reinterpret_cast<Object*>(destination);

My gut-feel says that that should work under all compilers for POD types. Whether it is cause for undefined behavior under some rules of the standard, I am not sure.

If that is cause for undefined behavior under some rules of the standard, you won't be able to save a POD-type to a binary file and read it from the binary file in a standards compliant manner. They rely on the bit-pattern written to a file and read from a file to represent the object.

The following program produces the expected result under g++.

#include <iostream>
#include <cstring>

struct Object
{
   int i;
   double d;
   Object(int ii, double dd) : i(ii), d(dd) {}
};

int main()
{
   Object o1(10, 20.34);
   char dest[sizeof(Object)];
   memcpy(dest, &o1, sizeof(dest));
   Object* ptr = reinterpret_cast<Object*>(dest);
   std::cout << o1.i << ", " << o1.d << std::endl;
   std::cout << ptr->i << ", " << ptr->d << std::endl;
}

Update, in response to OP's comments

The following program works as expected under g++.

#include <iostream>
#include <cstring>

struct Object
{
   int i;
   double d;
   Object(int ii, double dd) : i(ii), d(dd) {}
};

Object testFunction(Object o1)
{
   char dest[sizeof(Object)];
   memcpy(dest, &o1, sizeof(dest));
   Object* ptr = reinterpret_cast<Object*>(dest);
   return *ptr;
}

int main()
{
   Object o1(10, 20.34);
   Object o2 = testFunction(o1);
   std::cout << o1.i << ", " << o1.d << std::endl;
   std::cout << o2.i << ", " << o2.d << std::endl;
   o2.i = 25;
   o2.d = 39.65;
   std::cout << o2.i << ", " << o2.d << std::endl;
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • note that it would not be permitted to use an `Object` lvalue to access the destination subsequently – M.M Oct 07 '16 at 05:36
  • That isn't the same thing. That gets me a stack array with the same data, but not a stack object. – Andrew Spott Oct 07 '16 at 05:36
  • This is still an object that I can't return from a function... though the edit to the question to make that clear will have to wait. – Andrew Spott Oct 07 '16 at 05:54
  • @AndrewSpott, you sure can. Just use `return *ptr;`. – R Sahu Oct 07 '16 at 05:55
  • No, because destination will disappear when the function returns. – Andrew Spott Oct 07 '16 at 06:00
  • @AndrewSpott, That does not matter. You will return the contents of the array as a whole just as though it were an instance of `Object`. – R Sahu Oct 07 '16 at 06:01
  • I'm pretty sure that destination will be freed when the function returns and the pointer is pointing at nothing. – Andrew Spott Oct 07 '16 at 06:26
  • Sorry: this will work some of the time but is undefined behavior. The memory hasn't been overwritten in your example, but is no longer protected. – Andrew Spott Oct 07 '16 at 06:33
  • @AndrewSpott, your fears are unfounded. Like I said, if that is cause for undefined behavior, we won't be able to write to binary files and read from those binary files in a standards compliant manner. – R Sahu Oct 07 '16 at 06:37
  • @AndrewSpott notice that @R_Sahu is returning `*ptr`, which means he is returning the object by value, not by pointer. `o2` is a copy of the object that was originally memcpy'd into the local array dest. – harmic Oct 07 '16 at 07:05
2

because Object doesn't have a default constructor

When Object doesn't have a default constructor,

//Object o;//NG

there is no way to construct Object unless call another ctor or factory function. Because of that, you cannot call memcpy-like function.

When you have way to construct Object, to use memcpy-like function, Object class must grantee that it is trivially copyable class and standard-layout class(not equal to POD class).

trivial class : trivially copyable class && has no default user-defined constructor
POD class : trivial class && standard-layout class

yumetodo
  • 1,147
  • 7
  • 19
1

What you could do is simply use an array of bytes, then cast.

Note: what you are doing is not really good C++ practice, typically you should use assignment operators or copy constructors, and you should stick to the safer C++ casts rather than a brute-force C-style cast.

Anyway, that being said, this code will work:

class Object {
    int i;

public:
    Object(int i) : i(i) {}

    void foo() const {
        std::cout << i << std::endl;
    }
};

void bla() {
    Object one(1);
    char *bytes = new char[sizeof(Object)];
    memcpy(bytes, &one, sizeof(Object));
    Object &anotherOne = (Object &) *bytes;
    anotherOne.foo();
    const Object &oneMore = (Object) *bytes;
    oneMore.foo();
    Object *oneMoreTime = (Object *) bytes;
    oneMoreTime->foo();
    delete[] bytes;
}

The output is:

1
1
1

In summary, you need to allocate a region of memory on the stack or the heap that will become the Object instance.

Sean F
  • 4,344
  • 16
  • 30
  • Note: this is undefined behaviour. (May or may not appear to work on any particular implementation) – M.M Oct 07 '16 at 05:49
  • `(Object) *bytes` uses the first `char`'s value to invoke the constructor of `Object`, this is probably not what was intended – M.M Oct 07 '16 at 05:52
  • Not quite: C dictates that structs are allocated contiguously in memory, and so in C++ if the data fields are POD, as in this case, then this will work. However, in cases where the objects are not POD, you're right, this might not work. – Sean F Oct 07 '16 at 05:54
  • No , accessing `bytes`' content as `Object` violates the strict aliasing rule, regardless of POD-ness. The latter would be relevant if you constructed an `Object` and then memcpy'd content into the `Object`. – M.M Oct 07 '16 at 05:58
  • This also ignores the "without putting it on the heap" requirement. Along with directly accessing the memory, not doing it through the memcpy like function. – Andrew Spott Oct 07 '16 at 06:01
  • @Andrew Spott, I forgot that "heap" comment at the end of the question, but this code works the same off the heap, just use `char bytes[sizeof(Object)] `instead of `char *bytes = new char[sizeof(Object)]` – Sean F Oct 07 '16 at 06:06
  • Then you can't return the object... bytes gets freed. – Andrew Spott Oct 07 '16 at 06:10
  • @Andrew Spott Not if it is a global or static. And additionally, there was no part of the question saying that it cannot be stack-allocated because it must be returned. Stack-allocated objects can never be returned. – Sean F Oct 07 '16 at 06:13
  • Stack allocated objects can be returned through the copy constructor. And they act like C++ objects (destroyed when out of scope etc) which is what I would like. I'll try to update the question when I get a chance – Andrew Spott Oct 07 '16 at 06:29
  • @Andrew Spott If you use the copy constructor, then the returned object is not the same object, it's a copy. But yeah, you can do that if you want. Seems strange you would use a copy constructor in a question designed to explicitly avoid using copy constructors or assignment operators. – Sean F Oct 07 '16 at 06:37
  • @SeanF: Is the question unclear? Their is a specific reason I can't use assignment operators or copy constructors, and that reason only applies to getting the data from some other memory location, once the memcopy is done, those are all fair game. – Andrew Spott Oct 07 '16 at 16:10