views:

643

answers:

7

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?

A: 

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
+1  A: 

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
+1  A: 

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
At least it works in VC++7.
sharptooth
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
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
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
+1 - I'm going to try to combine this with Skizz's answer.
Matthew Murdoch
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
A: 

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
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
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
I haven't tried it. But according to me it won't work. However, the workaround suggested by sharptooth may work.
Naveen
+4  A: 

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

Skizz
+1 - I'm going to combine this with Tal's answer and give it a go.
Matthew Murdoch
A: 

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
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
+1  A: 

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

dwhall
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
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