8

I'm working in an embedded environment (Arduino/AVR ATMega328) and want to implement the Factory Method pattern in C++. However, the compiler I'm using (avr-gcc) doesn't support the new keyword. Is there a way of implementing this pattern without using new?

Matthew Murdoch
  • 30,874
  • 30
  • 96
  • 127
  • 2
    How do you allocate memory? D: – GManNickG Jun 23 '09 at 08:23
  • 2
    Aside from that, does it support placement new? The best I can think of is statically allocating a chunk of memory, then you can placement-new anything there. – GManNickG Jun 23 '09 at 08:23
  • 2
    Memory is either allocated on the stack (including stack-based object construction) or with malloc() - which doesn't support object construction. Placement new is not supported either... – Matthew Murdoch Jun 23 '09 at 08:27
  • Yes (see my previous comment). – Matthew Murdoch Jun 23 '09 at 08:37
  • 2
    I suppose there must be some way to call the constructor in-place. It maybe not standart-compliant, but it just should exists so that you have an equivalent of placement new. – sharptooth Jun 23 '09 at 08:39
  • While veering off topic, why doesn't it support new? – graham.reeds Jun 23 '09 at 09:08
  • If you can create objects on the stack, then it must be possible to new them if you define an operator new function. Stack objects must be correctly constructed, so the compiler is able to do that, the only thing new does that stack creation doesn't is to call operator new to get some memory. Stack objects already have there memory allocated. – Skizz Jun 24 '09 at 18:09
  • @graham.reeds Because generally the tiny amount of dynamic memory (sometimes less than 2KB, upon which the stack must also live) makes any kind of typical dynamic memory allocation a bad idea. Even `malloc` is recommended to be avoided. Plus the library designers decided to make the library C-like despite using a C++ compiler. – Pharap Aug 10 '17 at 18:57

7 Answers7

8

Since the AVR compiler is based on the gcc compiler, it is very likely to support the new keyword. What exactly is the error you're getting. I'm guessing it's a link/compiler error along the lines of an undefined function, namely, operator new. There is a difference between the new operator and operator new, the first is used to create objects and the latter is used to allocate memory for objects. The new operator calls operator new for the type of object being created, then initialises the object's v-table and calls the object's constructors. Reading this FAQ it says that operator new is not defined in the standard libraries. This is easy to fix, just define one:

void *operator new (size_t size)
{
  return some allocated memory big enough to hold size bytes
}

and you'll need to define a delete as well:

void operator delete (void *memory)
{
   free the memory
}

The only thing to add is the memory management, the allocation and freeing of blocks of memory. This can be done trivially, being careful not to clobber any existing allocated memory (the code, static / global data, the stack). You should have two symbols defined - one for the start of free memory and one for the end of the free memory. You can dynamically allocate and free any chunk of memory in this region. You will need to manage this memory yourself.

Skizz
  • 69,698
  • 10
  • 71
  • 108
4

The big picture of the Factory Method is object creation, which means heap memory consumption. On an embedded system, you are constrained by RAM and need to make all your design decisions with your memory limits in mind. The ATmega328 only has 2 KB RAM. I would recommend against using dynamically allocated memory in such a tight space.

Without knowing your problem in more detail, I would recommend statically declaring a handful of instances of the class and re-use those instances in some fashion. This means you need to know when and why your objects are created and--JUST AS IMPORTANT--when and why they end; then you need to figure out how many you need to have active at one time and how many it is possible to have active at one time.

!!Dean

sth
  • 222,467
  • 53
  • 283
  • 367
dwhall
  • 657
  • 1
  • 8
  • 13
  • All because it has 2KB or RAM doesn't mean you can't do dynamic memory allocation. You just can't allocate as much. You could get 500 two-byte objects in there (500*(2 for object+2 for allocation info) = 2000). – Skizz Jun 24 '09 at 18:04
  • Skizz, your first two sentences are correct, but the third one isn't reasonable. The 2 KB RAM (2048 bytes) holds both the C stack and the heap; so he would only have 48 bytes for his C stack (unreasonable). A program that needs a Factory Method is not a simple one, so his C call stack is going to vary in depth and could potentially overwrite objects in the heap. Dynamic memory allocation is possible, but not safe in such a small pool of memory. Static memory allocation is going to be easier to manage and more likely to yield a properly running program. – dwhall Jun 25 '09 at 17:14
3

If there is no way to instantiate a class at runtime, I suppose this isn't possible. All you could do is to pre-allocate some objects at compile time, create references to them and return them when needed.

kgiannakakis
  • 103,016
  • 27
  • 158
  • 194
1

What about something like this?

MyClass *objp = (MyClass*)malloc(sizeof(MyClass));
*objp = MyClass();  // or any other c'tor

EDIT: Forgot to mention, it assumes MyClass has an assignment operator.

EDIT2: Another thing I forgot - yes, there's a gotcha (it's C++, there are always gotchas). You'll have to call the d'tor manually for the object, since you can't use free.

Tal Pressman
  • 7,199
  • 2
  • 30
  • 33
  • 1
    My mother always warned me about using malloc() to instantiate a C++ object. Even though you are calling the constructor and copying its data to the allocated memory, are there any gotchas with this approach? – Matthew Murdoch Jun 23 '09 at 08:49
  • 1
    Yes, there are obvious gotchas: the memory block is uninitialized and so the assignment operator should react accordingly - if it sees an "initialized" pointer member it should not try to free it because it contains garbage and is dangling. I suppose it's best to just call calloc() to get zero-initialized memory block to avoid that. – sharptooth Jun 23 '09 at 08:54
  • 1
    Well, I'll leave clearing it and testing whether the allocation was even successful to production code. For all I know, maybe they want to allocate the memory from some memory pool and not malloc... – Tal Pressman Jun 23 '09 at 09:06
  • 2
    It's not going to work. Contrary to comment above the second line does not call any constructor on *objp, it just calls assignment operator on UNINITIALIZED object. If MyClass has any virtual methods or virtual bases it's going to blow at you sooner or later as virtual table pointer is not initialized. – Tomek Jun 24 '09 at 07:33
0

Can you do malloc? If so you can malloc your object that way.

Also what is the nature of your objects that you want to create from the Factory?

  • Are they imutable?
  • Is the factory only intended to produce a limited set of objects that can be known at compile time?

If the answer is yes to both questions, you can statically allocate the memory for your set of immutable objects and let the factory method return pointers to the appropriate object.

This will not work if the answer is no to either question. Also wuth this approach you have the problem of always having that memory allocated.

hhafez
  • 38,949
  • 39
  • 113
  • 143
0

If you are using factory means that you want some dynamic binding behavior which indicates that you have some virtual functions. Although, it may be possible to allocate the memory for the object using malloc() the vtable of the class will not be setup properly and hence the call to virtual functions will crash. I don't see any way of doing this when dynamic binding is required.

Naveen
  • 74,600
  • 47
  • 176
  • 233
  • That could be worked around - allocate an object on stack and call memcpy for the vtable pointer to copy it onto the newly heap-allocated object. – sharptooth Jun 23 '09 at 08:56
  • So are you saying that Tal's answer (http://stackoverflow.com/questions/1031301/can-i-implement-the-factory-method-pattern-in-c-without-using-new/1031375#1031375) won't work? – Matthew Murdoch Jun 23 '09 at 08:56
  • I haven't tried it. But according to me it won't work. However, the workaround suggested by sharptooth may work. – Naveen Jun 23 '09 at 09:11
0

A way I've solved this problem in an embedded system with strict coding standards (where we were not allowed to use "new" or "delete") was to create a static array of the desired object. And then use static pointers to the already-allocated objects, storing these returned values (using static variables and/or member variables) for later execution of the various objects.

// Class File ---------------------------------------------------
class MyObject {
    public:
        MyObject* getObject();

    private:
        const int MAX_POSSIBLE_COUNT_OF_OBJECTS = 10;
        static MyObject allocatedObjects[MAX_POSSIBLE_COUNT_OF_OBJECTS];

        static allocatedObjectIndex = 0;
};

// Implementation File ------------------------------------------

// Instantiate a static array of your objects.
static MyObject::allocatedObject[MAX_POSSIBLE_COUNT_OF_OBJECTS];

// Your method to return already created objects.
MyObject* MyObject::getObject() {

    if (allocatedObjectIndex < (MAX_POSSIBLE_COUNT_OF_OBJECTS - 1)) {
        return allocatedObjects[allocatedObjectIndex++];
    } else {
        // Log error if possible
        return NULL;
    }
}

Please be forewarned. This is all from memory as I haven't written any C++ in over 8 months.

Also Note: This has a serious drawback in that you are allocating a bunch of RAM at compile time.

Nate
  • 18,892
  • 27
  • 70
  • 93
  • You can define a MyObject::operator new (size_t) that does all the above, i.e. uses a pre-allocated array, whilst keeping the familiar new MyObject (args) syntax instead of something non-standard. That way, you can correctly construct and destruct objects. – Skizz Jun 24 '09 at 18:00
  • 4
    In an embedded system, I regard allocating a bunch of RAN at compile time a Good Thing, not a drawback – Martin Thompson Jul 04 '12 at 12:15