Improving a minimalistic OOP for microcontrollers using C, gcc, C99, and Macros with optimization -


often have program microcontrollers in c, because c++ compilers not available, or can not make extremely small code because of various bugs. often, oop "syntactic sugar", convenient when comes making programs hardware more encapsulated easy maintenance; wanted find out if there way oop syntax in c as possible oop overhead (when not needed) made optimize out in way portable. eg: optimize gcc targeted different microcontrollers, or perhaps using gcc's preprocessor , generic ansi-c compiler if gcc not available microcontroller.

i found threads, this, elegant way emulate 'this' pointer when doing oop in c? oop embedding pointers structs that's not want because wastes memory when i'm not interested in virtual methods, or that. can follow coding style in link features needed, want develop techniques when not needed; e.g. want able program using oop paradigms, simple easy understand code (not c++, though c++), , still able achieve minimal c program memory usage when oop paradigms not in use.

so, resorted experimentation gcc, , c99, because in general gcc 3.2 or above available platforms; , realized use sizeof() , typeof() compiler functions c99 index classes automatically ( 'trick' of sorts ) unused/uninitialized union member (so classes must unions sub-structs), in order access compile time constant lookup table created macros, bind data , methods, , guarantee type checking. etc. etc. etc.

eg: gcc allows optimizing out of const structures, , arrays, when members accessed constant expressions, thought might able use build macro based compile time binding system oop overhead handled in gcc , optimizes out of final binary.

with system, can variadic macro method calls, like: m( , init, "with", "any", "parameters", 7 ) looks variable a's type, call method init, using variable number of parameters...

see code examples below, , try them out -- it's simpler explanation: use gcc -e see macro expansions, , note ansi compilers, typeof() operator have replaced (void*)typecast; type checking works gcc.

the code cut , paste-able text editor, filename on first line, , compile , run on normal pc systems.

although did succeed in getting rid of individual pointers in every struct "point to" class's list of methods, saves memory in limited memory microcontroller, wasn't quite able figure out how compiler optimize out unused method pointers because had use (void*) pointers classes hold them in array, , require memory address (address of struct) , linker instance; , don't optimize out.

so: wondering if knew of way improve solution making kind of initialized method struct optimize out (have no linker address) after compilation, eg: when it's members accessed constant expressions in code. in essence i'm needing able element in array initialized portion of each array element different classxxx_mt, rather list of addresses classxxx_mt typecast (void*).

there's 2 other improvements i'd if can think of simple solution; cpp (c-pre-processor) doesn't allow defining of new macros within previous macro token concatenation (as far know), have make fixed length macro lists (a maximum of 10 in example) hold class definitions; means can have maximum of 10 classes in program; ideally, way make code more generic, cpp create variable length lists on fly. eg: problem related inability of c pre-processor "count" automatically.

and secondly, when try use anonymous structs newer versions of gcc, might rid of 'm' required access member data in iso-c eg: foo.m.mydata, deleting 'm' name class union definition, , compile gcc -std=c11 , gave me errors claiming struct defined nothing... so, anonymous structs inside unions don't work in gcc 4.8 although supposed to; how can anonymous structs work?

below example of how tested , implemented include file, voidbind.h, builds list of classes , statically links methods variables of class type.

ultimately, system allows me program example; compiled gcc 4.0 4.9 no problems:

//classtest.c #ifndef macrocheck  // don't macro expand stdio.h, it's ugly... #include <stdio.h>  // see macros, gcc -d macrocheck -e classtest.c #endif #include "class1.h" // include example class, library.  #define _void_finalize #include "voidbind.h" // make class list finalized, no more classes allowed  void main( void ) {     class1_ct a; // types ending in _ct macro created class types     class2_ct b;      m( , init ); // call method of variable, a, , function init.     printf("a=%s %s\n",a.m.name, m( b, tryme, "echo this" ) );      // i'd love rid of .m. in previous line using anonymous struct } 

next class definition / header file, both class1 , class2, showing how macro pre-processor used create classes of data bound methods , _ct type; broken 2 header files, , 2 libraries; i'm abusing header putting code simplicity.

//class1.h #ifndef _class1_h #define _class1_h   // define data type structure class1 typedef struct {     char* name;     int   one; } class1_t;  // define method type structure class1  union class1_ctt ; // class type tag, incomplete tag type class1_ct typedef struct { // method prototypes     void (*init)( union class1_ctt* ); // passed pointer class1_ct } class1_mt;  // bind class1_mt , class1_t class1_ct #define _void_new_class class1 #include "voidbind.h"  // begin class2 definition typedef struct { // define data type class2     int x; } class2_t;  union class2_ctt ; // class type tag, forward definition typedef struct { // method prototypes class2     char* (*tryme)( union class2_ctt*, char* echo ); } class2_mt;  // bind class2_t , class2_mt class2_ct #define _void_new_class class2 #include "voidbind.h"  // --------------------------------------------- start library code // separate file, , linked in // doing test, in header instead...  //#include <class1.h>  void class1_init( class1_ct* self ) {     self->m.name = "test";     self->m.one=5;   }  // define class1's method type (_mt) instance of linker data (_ld): // voidbind.h when creates classes, expects instance of // method type (_mt) named _mt_ld appended link prototyped // methods c functions.  actual "binding" information // , data can't "optimize out", eg: when there // more 1 method, , of them not used program  class1_mt class1_mt_ld = {     .init=class1_init };  // ----------- class2 libcode ----  char* class2_tryme( class2_ct* self, char* echo ) {     return echo; }  // class2's method type (_mt) instance of linker data (_ld). class2_mt class2_mt_ld = { // linker information method addresses     .tryme=class2_tryme };  // --------------------------------------------- end of library code  #endif 

finally, comes voidbind.h heart of system, getting cpp make compile time constant list of void* pointers method structs ... void* list optimize out, long passed in compile time constants. (but structs in list not optimize out. :( if constants. )

for idea work, had figure out way make cpp count how many times voidbind header file #included, in order automatically make list of class pointers, , since macro preprocessor can not addition, or define macros change based on previous definition of same macro name; had use inline functions "save" pointer class method struct (_mt) 1 pass next. that's forces me use void* pointers, though might solvable in way.

// voidbind.h // way build compile time void pointer arrays // these arrays lists of constants important @ compile // time , "go away" once compilation finished (eg:static bind). // example code written by: andrew f. robinson of scappoose   #ifdef _void_was_finalized //#{ #error voidbind_h included twice after _void_finalize defined #endif //#}  // _void_finalize, define after class headers have been included.  // simplify macro expansion output, , minimize memory impact // of optimization failure or disabling of optimization in bad compiler // in hopes of making program still work.  #ifdef _void_finalize //#{ #define _void_was_finalized #undef _void_bind static inline void* _void_bind( int x ) {     return _void_bind_obj[ x ]; } #else  // make sure file has data predefined binding before being // included, or else error out user knows it's missing define.  #if ! defined( _void_new_obj ) && ! defined( _void_new_class ) //#{ #error missing define of _void_new_obj or _void_new_class #endif //#}   // initialize macro (once) count number of times file // has been included; eg: since 1 object added void // list each time file #included. ( _void_objn )   #ifndef _void_objn //#{ #define _void_objn _error_void_objn_not_initialized_  // initialize, once, macros name concatenations  #define __void_cat( x, y ) x ## y #define _void_cat( x, y ) __void_cat( x , y )  // initialize, once, empty void* list of pointers classes, objs. #define _void_bind_obj (void* []){\     _void_obj0() , _void_obj1() , _void_obj2() , _void_obj3() , _void_obj4()\  ,  _void_obj5() , _void_obj6() , _void_obj7() , _void_obj8() , _void_obj9()\ } // define function macro return list, can // replaced _finalized  inline() function, later #define _void_bind(x) _void_bind_obj[ x ]  // void pointers null macros.  void list 0. #define _void_obj0()  0 #define _void_obj1()  0 #define _void_obj2()  0 #define _void_obj3()  0 #define _void_obj4()  0 #define _void_obj5()  0 #define _void_obj6()  0 #define _void_obj7()  0 #define _void_obj8()  0 #define _void_obj9()  0 #endif //#}  // figure out how many times macro has been called, // checking how many _void_objn() function macros have been // replaced inline functions  #undef _void_objn  #if defined( _void_obj0 ) // #{ #undef _void_obj0 #define _void_objn 0 #elif defined( _void_obj1 ) #undef _void_obj1 #define _void_objn 1 #elif defined( _void_obj2 ) #undef _void_obj2 #define _void_objn 2 #elif defined( _void_obj3 ) #undef _void_obj3 #define _void_objn 3 #elif defined( _void_obj4 ) #undef _void_obj4 #define _void_objn 4 #elif defined( _void_obj5 ) #undef _void_obj5 #define _void_objn 5 #elif defined( _void_obj6 ) #undef _void_obj6 #define _void_objn 6 #elif defined( _void_obj7 ) #undef _void_obj7 #define _void_objn 7 #elif defined( _void_obj8 ) #undef _void_obj8 #define _void_objn 8 #elif defined( _void_obj9 ) #undef _void_obj9 #define _void_objn 9  #else #error attempted define more ten objects #endif //#}  // ------------------------------------------------------- // if user defines _void_new_class // create union of 2 class structs, xxx_t , xxx_mt // , call xxx_ct.  must compatible xxx_ctt, tag // allows forward definitions in class headers.  #ifdef  _void_new_class //#{ #ifndef m  //#{ #define m( var , method , ... )\         (( (typeof(var._voidbind_t))_void_bind( sizeof(*(var._voidbind)) ) )->\         method( & var , ## __va_args__ )) #endif //#} extern _void_cat( _void_new_class , _mt ) _void_cat( _void_new_class , _mt_ld ); typedef union _void_cat( _void_new_class, _ctt ) {     char (*_voidbind)[ _void_objn ];     _void_cat( _void_new_class , _mt ) *_voidbind_t;     _void_cat( _void_new_class , _t ) m ; } _void_cat( _void_new_class , _ct );  static inline void* (_void_cat( _void_obj , _void_objn )) ( void ) {     return & _void_cat( _void_new_class, _mt_ld ); } #undef _void_new_class #else // ---------- otherwise, bind whatever object passed in static inline _void_cat( _void_obj , _void_objn ) (void) {     return (void*) & _void_new_obj ; } #undef _void_new_obj #endif //#}  // end of macros define list of pointers class method structures // , bind data types method types.  #endif //#} 

in general asking c++. examples posted going more efficient or equally efficient using c++ compiler.

often on embedded targets have far outdated versions of gcc generate bad code c++ or don't support gory c++ details.

you can try run ${your_arch_prefix}-g++ --nostdlib --nostdinc enable c++ syntax in parser without things waste space. if want disable other things can add -fno-rtti -fno-exceptions remove runtime type checking , exception support (see this question).

since c++ parser part of c front-end though c++ isn't officially supported micro controller vendor, might still working (sometimes can give try compile vendor specific version , add c++ languages in configure script).

this usally considered superior trying invent own oop macro dsl (domain specific language).

this being said if don't want go path , don't want use hand-craft vtables (as in your link). simplest thing have coding conventions. if don't want polymorphism code below sufficient. can define struct , functions in .c file , put declarations in headers. function below can called directly it's not in vtable, , first member this pointer in c++. struct impl actual data object holds not vtable or similar.

struct impl; struct impl *make_impl(); // don't use reserved keyword in c++ void do_bar(struct impl *mythis, int bar); 

if want polymorphism @ kernel does. explicitly embedd vtable in object , use macros extract them , initialze them.

look @ definition of char device instance.

and @ how people instanciate in code , headers. @ container_of macro , understand how media_entity_to_video_device casting works. (if little context you, @ book: linux device drivers (ldd3)).

i know code works , should proud understand doing. if show code other people, expect either write c or c++. if in c , missing oop try write code in way, others can grasp doing. using macros extract function pointers or polymorphic member fine, hiding function calls , generate structs in macros unreadable , people have debug code while running gcc -e see creations expanded preprocessor understand calling.

edit

i've had quick shot @ generating c code clang++. according this question , this one commands should be:

$ clang++ -std=c++11 -s -emit-llvm -o out main.cc # worked $ llc -march=c out  llc: error: invalid target 'c'.   $ clang++ --version  clang version 3.7.0 (trunk 232670) target: x86_64-unknown-linux-gnu thread model: posix 

it seems clang c backend has been removed (see these sources resurrecting c-backend code). being said have @ generating backend target plattform, think thats over-engineered.