2

I am working on a toy language for avr using C++ as the intermediate language, Problem is avr-gcc does not have new implemented. All my object derive from the class Object which has virtual methods when I create say a float object with malloc using instructions from,

Can I implement the Factory Method pattern in C++ without using new?

as soon as I cast it to an Object to pass around and back to a Float my program crashes, comments in the answer says that this is due to vtable not initializing properly, so how can i create a c++ object without using new and have the vtable setup properly?

Community
  • 1
  • 1
Hamza Yerlikaya
  • 49,047
  • 44
  • 147
  • 241

4 Answers4

5

It appears that new as a language construct is supported, but the operator new code that underlies it and does the actual allocation isn't implemented.

It should be pretty easy to fix that by providing your own implementation for operator new in your source code:

In some header file that's included in any file needing new

#include <stdlib.h> 

void * operator new(size_t size); 
void operator delete(void * ptr);

In a single cpp file.

void * operator new(size_t size) 
{ 
  return malloc(size); 
} 

void operator delete(void * ptr) 
{ 
  free(ptr); 
} 

Source: this post on avrfreaks.net, which also contains information about some other stuff you may want/need to implement on your own.

Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
  • @ken, That is what I tried from the above so thread but it crashed in gcc on os x. I am on the road no arduino at hand. – Hamza Yerlikaya Apr 10 '11 at 19:15
  • The ABI stuff like vtable, rtti, etc. are at negative offsets from the pointer. So, allocate something like 24 extra bytes and return a pointer to 24 bytes beyond malloc's returned pointer. – Heath Hunnicutt Apr 10 '11 at 19:32
  • @Heath: really? That sounds like it wouldn't work with arrays or stack allocation. – Ken Bloom Apr 10 '11 at 19:46
  • @Hamza: so now you accepted my message, meaning you eliminated the crash somehow. What was the problem, and how did you fix it? – Ken Bloom Apr 11 '11 at 13:26
  • 1
    Actually, the `operator new` implementation shouldn't be that simple. To have a fully standards conforming `new` implementation, you need to check the return from `malloc` and `throw std::bad_alloc` if it's null. – Ken Bloom Apr 11 '11 at 13:28
  • 1
    @Ken, I malloc d size of class without overriding new/delete to test quickly, that crashed, then back home tried your snippet and it worked. – Hamza Yerlikaya Apr 11 '11 at 19:49
  • @Hamza: probably because calling `malloc` directly doesn't call the constructor, and therefore doesn't set up the vptr. – Ken Bloom Apr 11 '11 at 19:57
3

It depends on the hardware platform you are compiling to, but the layout is usually pretty similar across implementations. After all, the first C++ was CFRONT, which compiled C++ to C...

The platform-dependent issues and the memory layouts will be described in a "platform C++ ABI" where ABI stands for "Application Binary Interface."

struct Cxx_ABI_Header
{
    unsigned inheritance_backward_offset;  /* Must be Zero for base object */
    unsigned rtti; /* Each class has its own signature. */
    void * vtable; /* Pointer to array of virtual function pointers. */
}

struct object_one
{
     char * file_name;
     int file_descriptor;
}

int object_one_create_file(struct object_one *);
int object_one_delete_file(struct object_one *);
int object_one_update_file(struct object_one *, off_t offset, 
                           size_t nbytes_replace, size_t nbytes_supplied,
                           char * buf);
int object_one_read_file(struct object_one *, off_t offset, 
                         size_t nbytes_read, char * buf);
int object_one_op_noauthz(struct object_one *)
{
    return ENOACCESS;
}

void * CRUD_vtable_authenticated_user = {
    { object_one_create_file, object_one_read_file,
      object_one_update_file, object_one_delete_file }};

void * CRUD_vtable_guest = {
    { object_one_op_noauthz, object_one_read_file,
      object_one_op_noauthz, object_one_op_noauthz }};

Here is a possible constructor, which actually makes two different kinds of "object_one".

struct object_one * new_object_one(char * filespec, int user_id)
{
    size_t n_bytes = sizeof(struct Cxx_ABI_Header) + sizeof(struct object_one);
    void * pheap = malloc(n_bytes);
    struct * pCxx_ABI_Header pcxx = pheap;
    struct * pObject  pobj = (void *)((char *)pheap 
                                               + sizeof(struct Cxx_ABI_Header));

    if (!pheap) ...

    pcxx->inheritance_backward_offset = 0;
    pcxx->rtti = /* You tell me? */
    pcxx->vtable = (userid < 0 ) ? CRUD_vtable_guest 
                                 : CRUD_vtable_authenticated_user;

    pobj->file_name = strdup(filespec);
    pobj->file_descriptor = 0;

    return pobj;
}

Voila - polymorphism via ?:

Anyway, enjoy the language experimentation and good luck improving on C++. By basing your efforts on C, you would be off to a solid start. ;)

Heath Hunnicutt
  • 18,667
  • 3
  • 39
  • 62
2

You can use malloc followed by placement-new which will create the object properly, constructing it at the location you specify.

Of course that still means using the keyword "new" but if what you really want to do is handle the memory / heap management and not put a total ban on that keyword then it might achieve what you are trying.

CashCow
  • 30,981
  • 5
  • 61
  • 92
1

If you want to tailor the implementation to one specific version of a specific compiler, then yes, for GCC simply look into the implementation of new in the GCC code.

Otherwise no. What you can do is two-factor initialization. First allocate the memory (malloc() or operator new()) and then initialize using placement new.

Šimon Tóth
  • 35,456
  • 20
  • 106
  • 151
  • He wants a solution without using new, and that means, I suppose, he doesn't want placement new either. – Nawaz Apr 10 '11 at 19:01
  • @Nawaz Well, I addressed that in the first sentence. – Šimon Tóth Apr 10 '11 at 19:02
  • @Nawaz: I don't share this; what rational reason would there be to _not_ use placement new to achieve the goal? – sehe Apr 10 '11 at 19:02
  • 1
    @sehe : OP said *Problem is avr-gcc does not have new implemented* – Nawaz Apr 10 '11 at 19:03
  • C++ provides no way to call a constructor for non-stack-allocated objects without using `new` or placement `new`. Placement `new` is probably broken in the same way as ordinary `new` and the fix should be reasonably straightforward. – Ken Bloom Apr 10 '11 at 19:09
  • @Nawaz: it's been a while since I used placement new, but if the compiler is a C++ compiler _at all_ it should be possible to come up with a manual implementation of placement new (placement new is manually implemented pretty much by definition) regardless of any (lack of) runtime library support?. Even if there is no heap library, just a naive block allocator on some kind of statically allocated data segment would do fine for simple cases – sehe Apr 10 '11 at 19:18
  • @sehe - One rational reason for not using placement new is implementing a new language using C, much as C++ was originally implemented, via CFRONT. – Heath Hunnicutt Apr 10 '11 at 19:25
  • @Heath: that logic/text completely evades me. This guy _IS_ using C++. – sehe Apr 10 '11 at 19:28
  • @sehe - He's working on some new language experiement, and he's using a C++ without new. So *HIS* reason for not using placement new is situational. But you asked for a rational reason, and I supplied one, without asserting that OP was employing that reason. I merely state that there does exist such a rational reason. – Heath Hunnicutt Apr 10 '11 at 19:30
  • @Heath: Ok, granted. By the way, I digged your backgrounder answer! – sehe Apr 10 '11 at 19:31