Static allocation of opaque data types

前端 未结 9 949
天涯浪人
天涯浪人 2020-11-27 13:50

Very often malloc() is absolutely not allowed when programming for embedded systems. Most of the time I\'m pretty able to deal with this, but one thing irritates me: it keep

相关标签:
9条回答
  • 2020-11-27 14:36

    You can use the _alloca function. I believe that it's not exactly Standard, but as far as I know, nearly all common compilers implement it. When you use it as a default argument, it allocates off the caller's stack.

    // Header
    typedef struct {} something;
    int get_size();
    something* create_something(void* mem);
    
    // Usage
    handle* ptr = create_something(_alloca(get_size()); // or define a macro.
    
    // Implementation
    int get_size() {
        return sizeof(real_handle_type);
    }
    something* create_something(void* mem) {
        real_type* ptr = (real_type_ptr*)mem;
        // Fill out real_type
        return (something*)mem;
    }
    

    You could also use some kind of object pool semi-heap - if you have a maximum number of currently available objects, then you could allocate all memory for them statically, and just bit-shift for which ones are currently in use.

    #define MAX_OBJECTS 32
    real_type objects[MAX_OBJECTS];
    unsigned int in_use; // Make sure this is large enough
    something* create_something() {
         for(int i = 0; i < MAX_OBJECTS; i++) {
             if (!(in_use & (1 << i))) {
                 in_use &= (1 << i);
                 return &objects[i];
             }
         }
         return NULL;
    }
    

    My bit-shifting is a little off, been a long time since I've done it, but I hope that you get the point.

    0 讨论(0)
  • 2020-11-27 14:40

    Unfortunately, I think the typical way to deal with this problem is by simply having the programmer treat the object as opaque - the full structure implementation is in the header and available, it's just the responsibility of the programmer to not use the internals directly, only through the APIs defined for the object.

    If this isn't good enough, a few options might be:

    • use C++ as a 'better C' and declare the internals of the structure as private.
    • run some sort of pre-processor on the headers so that the internals of the structure are declared, but with unusable names. The original header, with good names, will be available to the implementation of the APIs that manage the structure. I've never seen this technique used - it's just an idea off the top of my head that might be possible, but seems like far more trouble than it's worth.
    • have your code that uses opaque pointers declare the statically allocated objects as extern (ie., globals) Then have a special module that has access to the full definition of the object actually declare these objects. Since only the 'special' module has access to the full definition, the normal use of the opaque object remains opaque. However, now you have to rely on your programmers to not abuse the fact that thee objects are global. You have also increased the change of naming collisions, so that need to be managed (probably not a big problem, except that it might occur unintentionally - ouch!).

    I think overall, just relying on your programmers to follow the rules for the use of these objects might be the best solution (though using a subset of C++ isn't bad either in my opinion). Depending on your programmers to follow the rules of not using the structure internals isn't perfect, but it's a workable solution that is in common use.

    0 讨论(0)
  • 2020-11-27 14:42

    One way would be to add something like

    #define MODULE_HANDLE_SIZE (4711)
    

    to the public module.h header. Since that creates a worrying requirement of keeping this in sync with the actual size, the line is of course best auto-generated by the build process.

    The other option is of course to actually expose the structure, but document it as being opaque and forbidding access through any other means than through the defined API. This can be made more clear by doing something like:

    #include "module_private.h"
    
    typedef struct
    {
      handle_private_t private;
    } handle_t;
    

    Here, the actual declaration of the module's handle has been moved into a separate header, to make it less obviously visible. A type declared in that header is then simply wrapped in the desired typedef name, making sure to indicate that it is private.

    Functions inside the module that take handle_t * can safely access private as a handle_private_t value, since it's the first member of the public struct.

    0 讨论(0)
提交回复
热议问题