How to avoid malloc with encapsulation

I know encapsulation tecnique is a Good Thing in coding. Encapsulation means writing write well separated modules, similar to classes in OO languages.

Even in C, that isn't OO language, is possible to reach encapsulation by using a very simple ad effective trick: the instance of a "class" (C structure) is a pointer to the struct that isn't defined in .h (only typedef), but in .c. For example, see here [1].

IMHO it's a very good approach, however it's difficult to implement in embedded systems where you lack the presence of malloc (and you don't like to use it for many many reasons).

The constructor/init/create function of the class/struct must allocate the space needed for the instance, but it cannot use a static allocation method (it doesn't know how many instances will be requested).

One solution is to abandon encapsulation and expose the internal definition of struct in the header file. In this way, the user (of the class) can allocate directly the instance, by defining a variable.

Another solution is to implement a minimalistic malloc inside the class source, maybe a simple memory pool where the number of elements are defined at compile time.

Do you have better solutions?

[1]
formatting link
Reply to
pozz
Loading thread data ...

smart use of the preprocessor.

Bye Jack

Reply to
Jack

keskiviikko 21. joulukuuta 2016 13.38.04 UTC+2 pozz kirjoitti:

Typically using malloc is ok during system initialization time. An alternative to malloc are resource pools. Just allocate the needed resources when the system is booting up.

Br, Kalvin

Reply to
kalvin.news

The question is, when do you know, how many structs/objects are actually needed. If you can determine this at startup e.g. by checking existing hardware or reading a configuration file, thus you know how much is needed and allocate it from the stack or very primitive malloc (used once).

The problem gets harder, if you after five years of running, some additional allocations are needed. Even in this case, check if you can do the allocation from the stack.

Most processors these days have quite usable stack related addressing modes, thus not too badly cutting the performance.

Reply to
upsidedown

Op 21-Dec-16 om 12:38 PM schreef pozz:

Use C++.

(Just as with C, this means use *as suitable subset* of C++.)

Wouter "Objects? No Thanks!" van Ooijen

Reply to
Wouter van Ooijen

What's the problem with malloc()?

You are going to instantiate some object(s) of some class(es).

If they are ALL persistent, then use malloc to create them on *the* heap, as/when needed, and then never call free(). This is no different than static instances scattered around memory -- except that they're all in one place.

The issue comes when you want to create and then *destroy* individual instances (in any order imaginable) and are fearful of heap fragmentation (assuming you don't have more elaborate memory management mechanisms).

As your constructor knows the size/shape of the object, let its constructor allocate a "buffer" from a pool created for that class. Define the size of the pool (i.e., number of such class objects that can be instantiated) either at compile time *or* with a member function/variable that allocates the "pool" from The Heap (the *pool* will never be deleted so The Heap never experiences fragmentation as a result of that).

A cleaner approach is to write a generic malloc that can operate on any of several arenas. So, when creating the pool for ClassX, you use The System Heap as the arena. Likewise, when creating the pool for ClassY, ClassZ, etc.

Having created each of these (at system initialization time -- which allows you to defer making a decision regarding number of such objects of each class that might be required in this instance of the application), you now use the appropriate "class heap" (pool) as the arena when malloc()-ing buffers for each object instance of that type.

Make arenas "trivial" and you can create "local instances" on the stack, etc.

As the developer, if you happen to *know* that ClassX and ClassZ happen to require the same amount of memory to instantiate, then you could instead create a single heap (pool) from which you allocate *either* ClassX *or* ClassZ objects. This gives you some wiggle room if you underestimate the number of ClassX objects but overestimate the number of ClassZ objects.

At *some* point, you have to have a real numeric value for the number of X, Y and Z objects that your application requires. If you want total flexibility, then you need to manage them from a single heap. If you are fearful of fragmentation, then consider:

- making all objects the same size (padding as necessary)

- dynamically managing the objects with an extra level of indirection and letting a garbage collector shuffle them around in physical memory as *it* determines to be necessary (i.e., so it can "repack" the heap and move objects around to coalesce all free fragments into a single, large free-space)

E.g., I access each object through a "handle" so I don't care where (in virtual memory) it resides -- or even on which *node* it may currently exist. This lets the *code* move it around as the code sees fit -- instead of requiring me to keep track of where it

*should* be located.

Of course, all of these approaches have associated costs. There are no free lunches.

Reply to
Don Y

Yes, that's a good way to do encapsulation. I'd say just use C++ and don't use 'new', but the last time I tried using C++ on a processor with

8k of memory, I discovered that there's a ton of code that gets pulled in. Someone with a deeper understanding of newlib may have been able to stub all that off, but not I.

I can think of three approaches right off:

1: Statically allocate everything in the .c files, hard-coded.

This is easy enough if the 'objects' are directly connected to hardware entities like UARTs, and you're not reusing code across processors much.

2: Statically allocate everything in the .c files, from "config.h"

This is really 1a, but it lets you re-use your code, with a different "config.h" for each project.

3: Rewrite malloc and free.

Malloc just allocates blocks out of some free storage, and free calls assert (or does nothing, or pops a fault, or whatever). Better yet, if you can make the linker do it, don't link in free at all. You can't do this in C++ that I know of (at least with gnu C++ and newlib -- "delete" is too heavily embedded in the language), but in C you only pull in "free" if you explicitly use it. Then if someone down the road tries to use "free" the linker will whack them upside the head.

--
Tim Wescott 
Control systems, embedded software and circuit design 
 Click to see the full signature
Reply to
Tim Wescott

And never, ever, use free. This is what I do, even with C++. In general, the two problems with using the heap in an embedded system are memory fragmentation and outright using too much memory. If you never use free you'll never run into the former, and if you make sure that you only allocate memory at startup, then if you DO run into the latter it should happen during development, when it's easy to fix (it should be predictable, too, with a bit of care).

--
Tim Wescott 
Control systems, embedded software and circuit design 
 Click to see the full signature
Reply to
Tim Wescott

Me too.

Typically I used something like

void free( void * p ){ void you_silly_you_are_using_free(); you_silly_you_are_using_free(); }

without a definition of you_silly_you_are_using_free().

(That is, if I use a heap at all.)

Wouter "Objects? No Thanks!" van Ooijen

Reply to
Wouter van Ooijen

Am 21.12.2016 um 19:20 schrieb Tim Wescott:

I would worry about that approach unless it's strictly a single-person project. Letting people use malloc() but tabooing free(), OTOH, just feels a bit too much like handing a group of 5-year-olds some hand grenades after lecturing them there will be strictly no pulling of pins in this house.

In most real-world applications, I'm convinced that handing out objects from a statically allocated pool per "class" achieves both those goals just as easily, while requiring less memory overhead.

In light of that, the only truly valid excuse for using malloc()/new in a memory constrained embedded device that I've heard of so far would be a use case where these conditions apply:

0) memory is constrained (no "virtual" memory, no swapping) 1) the whole system is regularly started from scratch 2) several separate pools of objects exist 3) those pools are not fully utilized during every "run" 4) you can impose rigid upper limits on individual pools' sizes 5) the sum of upper limits always exceeds providable memory 6) the sum of actual utilization will _never_ exceed provided memory

I can imagine that systems exist that fulfill 0) to 5). I find it very hard to believe that the full combination of 0) to 6) exists in the wild. Condition 6) is effectively a unicorn --- the world might be nicer if it existed, but ...

Reply to
Hans-Bernhard Bröker

Il 21/12/2016 18:06, Wouter van Ooijen ha scritto: > Op 21-Dec-16 om 12:38 PM schreef pozz: >> I know encapsulation tecnique is a Good Thing in coding. Encapsulation >> means writing write well separated modules, similar to classes in OO >> languages. >> >> Even in C, that isn't OO language, is possible to reach encapsulation by >> using a very simple ad effective trick: the instance of a "class" (C >> structure) is a pointer to the struct that isn't defined in .h (only >> typedef), but in .c. For example, see here [1]. >> >> IMHO it's a very good approach, however it's difficult to implement in >> embedded systems where you lack the presence of malloc (and you don't >> like to use it for many many reasons). >> >> The constructor/init/create function of the class/struct must allocate >> the space needed for the instance, but it cannot use a static allocation >> method (it doesn't know how many instances will be requested). >> >> One solution is to abandon encapsulation and expose the internal >> definition of struct in the header file. In this way, the user (of the >> class) can allocate directly the instance, by defining a variable. >> >> Another solution is to implement a minimalistic malloc inside the class >> source, maybe a simple memory pool where the number of elements are >> defined at compile time. >> >> Do you have better solutions? > > Use C++. > > (Just as with C, this means use *as suitable subset* of C++.)

I know C++ could be better (maybe Ada or...). I think a discussion of the best programming language for embedded systems would be an entire different thread.

Staying on my question, how C++ helps? Of course it gives naturally encapsulation... but what about memory allocation and instance allocation?

Reply to
pozz

A lot of what I do in C++ is to instantiate my classes where I need them, statically in files.

So, for instance, my "serial.cpp" file doesn't instantiate any serial interfaces. Instead, my "menu.cpp" instantiates an object by including "serial.h" and then making an object that takes the port number in its constructor.

Wouter would have me write the serial port code such that the whole thing is a template class -- indeed, I may start doing that.

--
Tim Wescott 
Wescott Design Services 
 Click to see the full signature
Reply to
Tim Wescott

You can do a little better than that, by declaring free as:

void __attribute__((error("Don't use free"))) free(void*);

for gcc and compatible compilers, or

[[deprecated("Don't use free!")]] void free(void*);

for C++ 14, if you don't have gcc.

That won't help if free is called indirectly, in which case you are still relying on the linker failure. But at least for your own code that can #include these declarations, you'll get an error message (or at least a "deprecated" warning) at compile time.

Reply to
David Brown

For one thing, you override "new" for different classes so that you can get a safe pool-based allocation while still using "new" in your code.

For some classes, you will only ever have one of them. Such classes can have a static buffer, and use that for their "new" - that lets you have entirely statically allocated memory, but lets you use "new" to define the construction order.

Reply to
David Brown

Only when it needs parametrization.

Wouter "Objects? No Thanks!" van Ooijen

Reply to
Wouter van Ooijen

One technique that I have seen/used is the header that defines the opaque type also defines a structure that has been built to have the same alignment and size as the original, but not the 'real' definition (perhaps just a largest object then an array of char, or the right types but 'ugly' names (dummy1, dummy2, etc), and a check that its size matches in the real implementation file. That way client code can create a static object to pass to the init function that will have the right size, but the client code can't easily access the structure members.

Reply to
Richard Damon

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.