10

Often I have to program microcontrollers in C, because C++ compilers are often not available, or can not make extremely small code because of various bugs. But often, OOP "syntactic sugar", is very convenient when it comes to making programs for hardware more clearly encapsulated for easy maintenance; so I wanted to find out if there was a way to do OOP syntax in C where as much as possible the OOP overhead (when not needed) could be made to optimize out in a way that is portable. eg: That will optimize with gcc targeted for different microcontrollers, or perhaps by using gcc's preprocessor and a generic ANSI-C compiler if gcc is not available for that microcontroller.

I found only threads, like this, Elegant way to emulate 'this' pointer when doing OOP in C? which generally do OOP by embedding pointers into structs but that's not always what I want because it wastes memory when I'm not interested in virtual methods, or anything like that. I can always follow the coding style in the link where those features are needed, but I want to develop techniques for when they are not needed; e.g. I just want to be able to program using OOP paradigms, with simple easy to understand code (Not necessarily C++, though I like C++), and still be able to achieve a minimal C program memory usage when some OOP paradigms are not in use.

So, I resorted to experimentation with gcc, and C99, because in general gcc 3.2 or above is available for most platforms; and realized that I could use the sizeof() and typeof() compiler functions from C99 to index classes automatically ( a 'trick' of sorts ) from an unused/uninitialized union member (So classes must be unions with sub-structs), in order to access a compile time constant lookup table created by macros, which could bind data and methods, and guarantee all type checking. etc. etc. etc.

eg: GCC allows the optimizing out of const structures, and arrays, when their members are only accessed as constant expressions, so I thought I might be able to use that to build a macro based compile time binding system where the OOP overhead is handled in GCC and actually optimizes out of the final binary.

With this system, I can now do variadic macro method calls, like: M( a , init, "with", "any", "parameters", 7 ) which looks up variable a's type, call method init, using variable number of parameters...

See code examples below, and try them out -- it's simpler than the explanation: Use gcc -E to see the macro expansions, and note for ANSI only compilers, the typeof() operator will have to be replaced by a (void*)typecast; type checking only works with GCC.

The code is cut and paste-able into a text editor, with filename on first line, and it does compile and run on normal PC systems.

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

So: I was wondering if anyone knew of a way to improve my solution by making some kind of an initialized method struct which would optimize out (have no linker address) after compilation, eg: when it's members are only accessed as constant expressions in the code. In essence I'm needing to be able to look up an element in array where the initialized portion of each array element is a different classXXX_mt, rather than a list of addresses to classXXX_mt all typecast to (void*).

There's two other improvements I'd like help with if anyone can think of a simple solution; The cpp (c-pre-processor) doesn't allow defining of new macros from within a previous macro by token concatenation (As far as I know), so I have to make fixed length macro lists (A maximum of 10 in my example) to hold class definitions; which means I can only have a maximum of 10 classes in a program; but ideally, I would like a way to make my code more generic, so that the cpp could create variable length lists on the fly. eg: The problem is related the inability of the c pre-processor to "count" automatically.

And secondly, when I try to use anonymous structs for newer versions of GCC, so I might get rid of an extra 'm' required to access member data in ISO-C eg: foo.m.mydata, by deleting the 'm' name from the class union definition, and compile with gcc -std=c11 , it then simply gave me errors claiming the struct defined nothing... so, anonymous structs inside unions don't work even in GCC 4.8 although it supposed to; how can I get anonymous structs to work?

Below is the example of how I tested and implemented an include file, voidbind.h, which builds a list of classes and statically links the methods to the variables of that class type.

Ultimately, the system allows me to program like this example; which I compiled with gcc 4.0 to 4.9 with no problems:

//classtest.c
#ifndef MACROCHECK  // Don't macro expand stdio.h, it's ugly...
#include <stdio.h>  // to see macros, do 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 are the macro created class types
    class2_ct b;

    M( a , init ); // Call method of variable, a, and the function init.
    printf("a=%s %s\n",a.m.name, M( b, tryme, "echo is this" ) ); 
    // I'd love to be rid of .m. in the previous line using anonymous struct
}

Next is the Class definition / header file, for both class1 and class2, showing how the macro pre-processor is used to create classes of data bound to methods and the _ct type; normally this would probably be broken up into two header files, and two libraries; but I'm just abusing the header by putting all the code together for simplicity.

//class1.h
#ifndef _class1_h
#define _class1_h


// Define the data type structure for class1
typedef struct {
    char* name;
    int   one;
} class1_t;

// Define the method type structure for class1 
union class1_ctt ; // class type tag, incomplete tag type for class1_ct
typedef struct { // method prototypes
    void (*init)( union class1_ctt* ); // passed a pointer to class1_ct
} class1_mt;

// bind class1_mt and class1_t together into class1_ct
#define _VOID_NEW_CLASS class1
#include "voidbind.h"

// Begin class2 definition
typedef struct { // define data type for class2
    int x;
} class2_t;

union class2_ctt ; // class type tag, forward definition
typedef struct { // method prototypes for class2
    char* (*tryme)( union class2_ctt*, char* echo );
} class2_mt;

// bind class2_t and class2_mt together into class2_ct
#define _VOID_NEW_CLASS class2
#include "voidbind.h"

// --------------------------------------------- Start library code
// This would normally be a separate file, and linked in
// but as were doing a test, this is in the 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 it creates classes, expects an instance of the
// method type (_mt) named with _mt_ld appended to link the prototyped
// methods to C functions.  This is the actual "binding" information
// and is the data that I can't get to "optimize out", eg: when there
// is more than one method, and some of them are not used by the 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 for method addresses
    .tryme=class2_tryme
};

// --------------------------------------------- End of library code

#endif

Finally, comes voidbind.h This is the heart of the system, Getting the CPP to make a compile time constant list of void* pointers to method structs ... the void* list will always optimize out, as long as everything passed in are compile time constants. (But the structs in the list will not completely optimize out. :( even if constants. )

For this to idea to work, I had to figure out a way to make cpp count how many times the voidbind header file was #included, in order to automatically make a list of class pointers, and since the macro preprocessor can not do addition, or define macros which change based on a previous definition of the same macro name; I had to use inline functions to "save" the pointer to the class method struct (_mt) from one pass to the next. That's what forces me to basically use void* pointers, though it might be solvable in another way.

// voidbind.h
// A way to build compile time void pointer arrays
// These arrays are lists of constants that are only important at compile
// time and which "go away" once the compilation is finished (eg:static bind).
// Example code written by: Andrew F. Robinson of Scappoose


#ifdef _VOID_WAS_FINALIZED //#{
#error voidbind_h was included twice after a _VOID_FINALIZE was defined
#endif //#}

// _VOID_FINALIZE, define only after all class headers have been included. 
// It will simplify the macro expansion output, and minimize the memory impact
// of an optimization failure or disabling of the optimization in a bad compiler
// in hopes of making the 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 this file has data predefined for binding before being
// included, or else error out so the user knows it's missing a define.

#if ! defined( _VOID_NEW_OBJ ) && ! defined( _VOID_NEW_CLASS ) //#{
#error missing a define of _VOID_NEW_OBJ or _VOID_NEW_CLASS
#endif //#}


// Initialize a macro (once) to count the number of times this file
// has been included; eg: since one object is to be added to the void
// list each time this file is #included. ( _VOID_OBJn ) 

#ifndef _VOID_OBJn //#{
#define _VOID_OBJn _ERROR_VOID_OBJn_NOT_INITIALIZED_

// Initialize, once, macros to do name concatenations 
#define __VOID_CAT( x, y ) x ## y
#define _VOID_CAT( x, y ) __VOID_CAT( x , y )

// Initialize, once, the empty void* list of pointers for 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 a function macro to return the list, so it can be easily
// replaced by a _FINALIZED  inline() function, later
#define _VOID_BIND(x) _VOID_BIND_OBJ[ x ]

// All void pointers are initially null macros.  So the void list is 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 this macro has been called, by
// checking for how many _VOID_OBJn() function macros have been
// replaced by 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 to define more than ten objects
#endif //#}

// -------------------------------------------------------
// If the user defines _VOID_NEW_CLASS
// Create a union of the two class structs, xxx_t and xxx_mt
// and call it xxx_ct.  It must also be compatible with xxx_ctt, the tag
// which allows forward definitions in the 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, just bind whatever object was passed in
static inline _VOID_CAT( _VOID_OBJ , _VOID_OBJn ) (void) {
    return (void*) & _VOID_NEW_OBJ ;
}
#undef _VOID_NEW_OBJ
#endif //#}

// End of Macros to define a list of pointers to class method structures
// and to bind data types to method types.

#endif //#}
Community
  • 1
  • 1
  • 1
    As I said, g++ is often not available on all microcontroller platforms. And the code not only compiles, it runs just fine. class1_ct, is actually defined by the binding macro in voidbind.h, and creates a union which binds class1_t and class1_mt together. class1_ctt is an incomplete type which is the same as class1_ct will be defined as, so that function prototypes can be made before including voidbind.h. – Andrew of Scappoose Apr 28 '15 at 04:01
  • I know this doesn't answer the question, but I feel compelled to ask: if you succeed, what advantage does that present? OOP is certainly possible in C, and in fact is used in the real world. But I really don't see what the objective is here. Why not just do `class1_init(&a);` and `class2_tryme(&b)`? You don't seem to be interested in the features that are usually associated with OOP (like polymorphism)... – Cornstalks Apr 28 '15 at 04:07
  • 1
    The code is simpler to read. And generally, I don't need to carry around longer names all over the place to distinguish classes, which is exactly what you are doing; For when I write the code, I simply say M( a, init ) and WHATEVER the type of a -- it automatically picks the correct class; so if I change the class type for 'a', the code is all still going to be correct throughout the rest of my program(s). I would have to hand re-edit it, using your technique Note: Being able to bind statically, which is what I want here, doesn't prevent additional mechanisms for polymorphism later. – Andrew of Scappoose Apr 28 '15 at 04:13
  • 2
    Ugh. Unreadable as hell. I won't even try to understand it. As @Cornstalks says, no polymorphism means no OOP, so it's still not clear what you are trying to achieve. – n. m. could be an AI Apr 28 '15 at 04:17
  • A is an object that has both data and methods. So, yes, it is OOP. And yes, I can do polymorphism if I want to. I'm just not concentrating on that in this example. – Andrew of Scappoose Apr 28 '15 at 04:18
  • "[...] doesn't mean I can't also do dynamic bindings when I desire, and implement polymorphism." If you want dynamic bindings and polymorphism, you pretty much have to implement something like a vtable (which is something you've said you can't accept). At this point, all you really want is syntactic sugar to hide some of C's verbosity. I don't have any creative ideas to help you, but my purpose in asking these questions was to clarify some of the goals that weren't clear in the question. – Cornstalks Apr 28 '15 at 04:22
  • 1
    No. your *objects* don't have methods. Your *static types* have methods associated with them. For OOP you need to associate methods with (run time values of) object themselves. The correct method should be selectable *at run time* not at compile time. This is called "late binding" and it *the single unique feature* that characterizes OOP. – n. m. could be an AI Apr 28 '15 at 04:28
  • I don't want to be FORCED to use Vtables in all cases. Right now in the example I am giving, I already DO have a class which has a table of methods which could be dynamically changed... so it's not like I can't do polymorphism, I just want to be ABLE to optimize out the table when it's not needed. – Andrew of Scappoose Apr 28 '15 at 04:30
  • If you don't want to use a vtable, don't use one, just don' claim it's still OOP. It isn't. If you want to do some static compile-time overloading in C, say so in the question, don't mention OOP and all shoulf be nice and clear. – n. m. could be an AI Apr 28 '15 at 04:36
  • 1
    That's simply not correct. OOP as historically defined does not always require vtables. Your confusing C++ with the general notion of OOP. They are not the same. There are many other versions of OOP. http://en.wikipedia.org/wiki/Object-oriented_programming – Andrew of Scappoose Apr 28 '15 at 04:42
  • *Your confusing C++ with the general notion of OOP.* This follows from nowhere. *OOP as historically defined does not always require vtables.* You are certainly free to use this definition, just mention it in the question itself, so the rest of us who subscribe to the it may skip. The first language that called itself OO was Smalltalk, which had late bindings for everything. So much for the historical definition. – n. m. could be an AI Apr 28 '15 at 04:59
  • Quoting from the wiki article you linked, *An object is an abstract data type with the addition of polymorphism and inheritance*. and then *fundamental features that support the OOP programming style in most object-oriented languages:[20] Dynamic dispatch – when a method is invoked on an object, the object itself determines what code gets executed by looking up the method at run time*. Feel free to ignore this of course. – n. m. could be an AI Apr 28 '15 at 05:08
  • And smalltalk is not the only OOP language historically defined. Later languages can do early binding as an optimization. Since the thread is about "optimizing" that should be a big hint to most thoughtful people about what's going on. YES-- with the "addition" not the built in support for. eg: "abstract data type". – Andrew of Scappoose Apr 28 '15 at 05:11
  • "Later languages can do early binding as an optimization". If a compiler can optimize out a vtable access here and there, more power to it. A langiage that has no late binding at all is not OOP. Programming without late bindings is not object oriented, plain and simple. You are not optimising late bindings out, you simply don't include them in your framework. And if you add them later on, your macro system sure as hell won't be able to optimise them out when they are not actually needed. – n. m. could be an AI Apr 28 '15 at 05:20
  • 1
    The actual definition of OOP is *wildly* off-topic here... what matters is whether OP a) has a clear idea of what they want to achieve and b) can express it unambiguously in the question. (it sounds somewhat like [static polymorphism](http://stackoverflow.com/questions/19062733/what-is-the-motivation-behind-static-polymorphism-in-c) to me, or maybe overloading) – Alex Celeste Apr 28 '15 at 05:34
  • 1
    This is just bad. You falling into the classic trap of trying to invent a new language with the preprocessor. Use classic C conventions, don't #define yourself into a hybrid C language. If you are super keen on this, then you could write your own hybrid language that spits out C code to be compiled by your uC compiler. – Keith Nicholas Apr 28 '15 at 05:44
  • Keith, yes -- in fact I could. And in many ways, I agree with you. It's a recurring problem and it can easily go too far / bad. I originally was looking to see if there was an already available language such as objective C, which would do as you suggest and spit out a C program that I could compile, but in general -- all such languages that I found either require a special (and large) run-time library, or else don't optimize well at all. Given my preferences, I WOULD just use c++, but it's not available, and the front ends make very difficult to debug code in C. – Andrew of Scappoose Apr 28 '15 at 05:52
  • 1
    If you are using gcc probably you can use clang to write c++ and let clang generate C code for you. – Alexander Oh Apr 28 '15 at 06:12
  • What Keith says. `myobject->ops.method(myobject, parm1, parm2)` is the way to write a vtable-based call. `myclass_ops.method(myobject, parm1, parm2)` is a no-vtable call. Either one is readable, maintainable and just plain old working. No macros. A macro system like yours may be somewhat nice to plain with, but in the end of the day it's simply not suitable for production. – n. m. could be an AI Apr 28 '15 at 07:02
  • The point of the thread is to remove any memory not in use when it is not needed. It's a huge waste of MEMORY in a microcontroller, to keep something like an entire 20 element vtable, if the program only called 1 of the methods listed in it, ever. Nor are there really serious maintenance problems with macros like I'm showing -- for macros are often used for "sugar" even in the Linux kernel. And besides, if I want to do something "virtual" rather than "static" all I need to do is pass in the address of a variable to the macro, rather than a constant, and the C compiler won't optimize it out. – Andrew of Scappoose Apr 28 '15 at 07:43
  • What you want can be easily done with C11 _Generic feature and a couple of easy readable macros. You need a fairly recent gcc (4.9 I think). – n. m. could be an AI Apr 28 '15 at 09:29
  • Sorry, I meant "what you are currently doing", not "what you want to do". – n. m. could be an AI Apr 28 '15 at 09:47
  • The main reason why embedded MCUs lack a C++ compilers or GCC port is that the architecture is diverge from the modern RISC convention and is fundamentally hostile to the (function-)pointer-happy your macros generate. Therefore if performance is the goal then consider dispatching through switch statements and accessing objects by copying the active instance into/out-of an single shared variable. Or at the least take a close look at what type of constructs your system is comfortable with. – doynax Apr 28 '15 at 10:31
  • TL;DR, but check the plan9-extensions of gcc (I can assure it's available in gcc since 4.7 at least). – too honest for this site Aug 17 '18 at 19:07

4 Answers4

6

In general what you are asking for is C++. the examples you posted are most likely going to be the more efficient or equally efficient using a C++ compiler.

Often on embedded targets you have far outdated versions of gcc that generate bad code for c++ or don't support all the gory c++ details.

You can try to run ${your_arch_prefix}-g++ --nostdlib --nostdinc which will enable c++ syntax in the parser without all the things that waste space. if you want to disable other things you can add -fno-rtti -fno-exceptions with remove runtime type checking and exception support (See this question).

Since the C++ parser is part of the C front-end even though C++ isn't officially supported by your micro controller vendor, this might still be working (sometimes you can also give it a try to compile the vendor specific version yourself and add c++ to the languages in the configure script).

This is usally considered superior to trying to invent your own OOP like macro DSL (domain specific language).

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

struct impl;
struct impl *make_impl();
// don't use this as it is a reserved keyword in c++
void do_bar(struct impl *myThis, int bar);

If you want polymorphism look at what the kernel does. they explicitly embedd the vtable in the object and use macros to extract them and initialze them.

look at the definition of char device for instance.

and look at how people instanciate this in code and headers. Look at the container_of macro and understand how media_entity_to_video_device casting works. (If this is too little context for you, look at this Book: Linux Device Drivers (LDD3)).

I know your code works and you should be proud to understand what you are doing. But if you show your code to other people, they expect you to either write C or C++. If you are in C and are missing OOP I would try to write the code in a way, that others can grasp easily what you are doing. Using macros to extract function pointers or get a polymorphic member is usually fine, hiding function calls and generate structs in macros is often unreadable and people have to debug your code while running gcc -E to see your creations expanded from the preprocessor to understand what they are actually calling.

Edit

I've had a very quick shot at generating C code from clang++. according to this so question and this one the 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 the clang C backend has been removed (see also these sources resurrecting the C-backend code). That being said you could also have a look at generating a backend for your target plattform, but I think thats definitely over-engineered.

Community
  • 1
  • 1
Alexander Oh
  • 24,223
  • 14
  • 73
  • 76
  • Thanks for the comment on clang, I will look into that ; but I didn't know about it before -- There is no g++ binary in a few of the microcontroller packages I have, although I appreciate your outlining the flags like that. But, I take it I can't get this result with a flag passed to the binary gcc ? I would think that my mistake, mostly, in the opening post is in not commenting and explaining the code's conventions; so I've tried to fix that in the original post. But yes, it's best to use gcc -E to understand how it works. – Andrew of Scappoose Apr 28 '15 at 11:09
  • if you name the compilers you use I can have a quick look. I remember doing c++ on AVR. – Alexander Oh Apr 28 '15 at 11:50
  • 1
    and have you tried feeding the compiler a .cc or .cpp file? that might trigger the parser to use c++ (without the c++ runtime) – Alexander Oh Apr 28 '15 at 12:02
  • 1
    "no g++ binary" The vendor must supply the sources they used to build their binaries (the GPL thing). You can try to build a more complete GCC installation that includes g++ from these sources. – n. m. could be an AI Apr 28 '15 at 12:58
  • It doesn't make sense to create a g++ binaries for certain target chips; for example the microchip 16C5x series chips, unlike the32 bit processors, do not even have a stack, let alone the ability to jump to a ram based address. Hence, vtables in c++ can't be used, and the C compiler, I think it was called picgcc the last time I tried it, is strictly a subset of C implementation, using unusual techniques to work around not having a stack, and no g++ BUT if a function pointer doesn't go to a variable address, it will produce a ROM jump. So, there's hope for a C based solution, ! C++ – Andrew of Scappoose Apr 28 '15 at 18:08
  • alex: I tried to pass a .cc file with "class one { int x; };" as it's only line to gcc on my PC x86, and yes it worked. So -- cool! I'll try it on some of my microcontroller targets later today and see what happens. :) – Andrew of Scappoose Apr 28 '15 at 18:23
  • 1
    @user2133679 about the doesn't make sense to use C++ on embedded. some meta programming techniques involving templates might be interesting, because they allow static polymorphism without wasting any space in a binary for instance. the same goes for function overloading. also interesting is the fact that the compiler isn't forced to generate the complete class if some functions aren't used when the class is a template class. – Alexander Oh Apr 30 '15 at 07:10
  • Yes Alex. You and I think alike... I was just thinking that my macro system also allows people to do templates in C, by #defines -- and then #including the template file -- for exactly the same reasons. -- note, I tried the .cc extension on a couple older gcc packages, and it didn't work. So apparently it's hit and miss depending on how the binary tools were originally compiled. But it is cool that it sometimes works. – Andrew of Scappoose Apr 30 '15 at 07:57
1

For the side question, you can use -std=gnu99 to get C99 with gnu extensions (like anonymous struct and union members within structs and unions).

luser droog
  • 18,988
  • 3
  • 53
  • 105
  • No good :( The result of deleting the 'm' name in the union on line 122 of voidbind.h, and .m. on line 14 of classtest.c, and lines 44 and 43 of class1.h, and then compiling with gcc -std=gnu99 or gnu11 leads the same error: voidbind.h:122:39: warning: declaration does not declare anything [enabled by default] --- The union itself, after gcc -E, looks like: typedef union class1_ctt { char (*_VOIDBIND)[ 0 ]; class1_mt *_VOIDBIND_T; class1_t ; } class1_ct; _VOID_CAT( _VOID_NEW_CLASS , _t ) ; – Andrew of Scappoose Apr 28 '15 at 10:55
  • Edit malfunctioned; the final _VOID_CAT() is not supposed to be there. sorry. – Andrew of Scappoose Apr 28 '15 at 11:01
  • I know that GCC, and my version is supposed to do anonymous unions, and structs, so I don't understand why with the compiler flags passed, like you show, that I'm still getting "declaration does not declare" warnings, and then proving it's been removed, saying "class1_ct" has no member named 'name'; Is there a simple example of anonymous struct inside a union perhaps unrelated to my solution, which you KNOW at least compiles on gcc 4.6.0 and above ? eg: an example I can look at ? – Andrew of Scappoose Apr 28 '15 at 18:44
1

The question mentions -std=c11, so I guess that use of _Generic is OK in this situation.

Since what you appear to be asking for is a way to statically resolve methods from a shared name based on argument type, it makes some sense to look at overloading(/static polymorphism/ad-hoc polymorphism/etc.) as the basis for your system's operation, rather than trying to optimize a pattern generally intended for runtime resolution. _Generic is a static type->value selection operator that is intended specifically for helping with situations like this. It allows you to macro-expand the type-selection code directly into the calling expression and guarantees it will be removed at compile-time, which is exactly what you need.

Since it's an expression operator, _Generic has to list all of the types it's going to operate on in the expression. This means something has to be clustered, which isn't a perfect fit for your OOP strategy. Conventional overloading strategies cluster the function definitions, which would mess up trying to organize methods into classes; however, if you're willing to make an explicit list of all classes in use in your program (i.e. cluster the types instead) it should still be possible to achieve static resolution in a similar way.

e.g. (rough example):

#include <stdio.h>

// shared method table structure for all classes
typedef struct {
    void (* init)( void* );
    char* (* tryme)( void*, char* echo );
} poly_method_table;

// define class1
typedef struct {
    char* name;
    int   one;
} class1;
void class1_init( class1* self ) {
    self->name = "test";
    self->one=5;  
}
const poly_method_table class1_mt = {
    .init = class1_init
};

// define class2
typedef struct {
    int x;
} class2;
char* class2_tryme( class2* self, char* echo ) {
    return echo;
}
const poly_method_table class2_mt = {
    .tryme = class2_tryme
};

// global lookup table
const poly_method_table * table_select[] = {
    &class1_mt,
    &class2_mt,
};
#define M(MSG, THIS, ...) table_select[_Generic((THIS), \
    class1 *: 0, \
    class2 *: 1, \
    default: "error")]->MSG((THIS), ## __VA_ARGS__)


int main( void ) {
    class1 a;
    class2 b;

    M( init, &a );
    printf("a=%s %s\n",a.name, M( tryme, &b, "echo is this" ) );
}

The method operator M produces a constant lookup value into the global table-of-vtables (instead of trying to retrieve the vtable from the object itself). With enough const declarations I would expect a decent optimizer to be able to remove this and go straight to the selected function, since there's no runtime variance in which vtable gets selected.

Since you're already using GNU extensions (i.e. ,## for method calls), you could improve this by using typeof to cast the vtable lookup to a specialized type for each class (instead of having a single vtable class that supports all polymorphic method names), potentially reducing size somewhat and making room for further overloading at the method level.

You could remove the annoying repetition in the definitions of table_select and M with a FOR_EACH macro (it would automatically fill out the table, the middle of the _Generic block, and an enum to build indexes), e.g.:

#define CLASSES class1, class2 //etc.

#define BUILD_ENUM(class) class ## _enum,
#define BUILD_SELECTOR(class) &class ## _mt,
#define SELECT_CLASS(class) class *: class ## _enum,

#define M(MSG, THIS, ...) table_select[_Generic((THIS), \
  FOR_EACH(SELECT_CLASS, CLASSES) \
  default: "error")]->MSG((THIS), ## __VA_ARGS__)

enum { FOR_EACH(BUILD_ENUM, CLASSES) };
const poly_method_table * table_select[] = {
    FOR_EACH(BUILD_SELECTOR, CLASSES)
};

(you can find suitable definitions of FOR_EACH elsewhere on SO)

Community
  • 1
  • 1
Alex Celeste
  • 12,824
  • 10
  • 46
  • 89
  • It's OK to consider -std-C11 solutions, although any GCC solution which doesn't work from all of GCC 3.2 onward is going to be a partial solution based on my OP. I was thinking of making my macro's switch on additional features based on which GCC version is involved in the compile so that the basic binding works on any of GCC 3.2 forward, but where additional features could be enabled if a more powerful version of GCC was available. eg: there is no memory penalty for having a union of an anonymous struct, and a named struct, both -- so compatibility is possible with type safe upgrades. – Andrew of Scappoose Apr 28 '15 at 19:30
  • Hmmm... What version of GCC started allowing FOR_EACH ? as I don't see it in the C11 standard notes; and also when I compile the rough example code, gcc -std=c11 crude.c ; it bombed with "crude.c:39:5 error expected expression before class1_t." which is right in the _Generic macro call. I don't see anything wrong before class1_t... so I'm not sure what's going wrong. – Andrew of Scappoose Apr 28 '15 at 22:10
  • 1
    @user2133679 `FOR_EACH` isn't predefined, but needs to be defined in your code or in an included library ([example implementation](http://stackoverflow.com/questions/26568721/compile-time-generated-block-in-c/26568922#26568922)). With that, the above code should work in GCC 4.9+. (initial version of the second block wasn't tested, just typed into SO - I have corrected it now) – Alex Celeste Apr 28 '15 at 22:36
  • If I install gcc 4.9.2, it's able to compile, but not link. So the compile errors in the first example was a compiler issue ... and unfortunately on my 32/64 bit distro, I can't get your program to link with 4.9.2 -- some kind of mulilib bug... not your fault. It's weird, though, that 4.8.2 -- can't compile it, for it was released in October of 2013, which is >> std=c11 ; So I take it 4.9.x has some kind of bug fix ? – Andrew of Scappoose Apr 30 '15 at 07:23
  • The original OP, if you look at the code, uses a macro to embed the constant which you are computing in _Generic automatically. So, I don't see that you are really improving the system by using _Generic because table select is still a list of typecast pointers, which means the _mt structures have to be passed to the linker -- and can't be optimized out. At least not by gcc 3.2; To avoid the linker, I need a list of instantiated structs that we never take the address of. :) Your idea of a shared method table gave me an idea to try, for the poly table could be a union of class unions... – Andrew of Scappoose Apr 30 '15 at 07:50
  • @user2133679 oooohhhhh I think I misunderstood the original problem a bit. Perhaps `_Generic` does have a way to help you though - a *two-part* selector, dispatching first on the class and then on a *placeholder type* defined for each method name? Might try to write that up later. – Alex Celeste Apr 30 '15 at 15:06
  • I'd appreciate that; Your post was very helpful in me figuring out how to clarify the nature of the problem; so I edited the OP to reflect a more concise explanation of the optimization failure; hopefully it's less confusing ? – Andrew of Scappoose May 01 '15 at 05:57
1

If you're willing to forgo any runtime polymorphism, you can completely get rid of your method table objects by replacing them with a _Generic structure simulating a compile-time dispatch table. You can first dispatch on the declared type of an object to choose its static method table, and then dispatch on a dummy type declared to match the method name, to resolve the actual method to call. Basic structure:

#define M(MSG, THIS, ...) _Generic((THIS), \
  class1 *: class1_selector((struct class1_ ## MSG ## _dmy*)0), \
  class2 *: class2_selector((struct class2_ ## MSG ## _dmy*)0), \
  default: "error")(THIS, ## __VA_ARGS__)

(note: there is a reason why I reversed the THIS/MSG operands, explained below)

The method call operator M is built around a centralized list of all the classes in the program. It dispatches on the THIS pointer to select a classX_selector macro to call. It passes the selector a dummy pointer of a type named around the method (cast from zero is fine, we're not going to use it anyway).

#define class1_selector(MSG) _Generic((MSG), \
  struct class1_init_dmy *: class1_init, \
  struct class1_show_dmy *: class1_show, \
  struct class1_getOne_dmy *: class1_getOne, \
  default: "error")

The classX_selector macro expands to a static dispatch table for all of the methods supported by that class. In this case, class1 is defined to support three methods init, show and getOne. The type of the dummy pointer is used to select a method using another type dispatch table. The method is returned, becomes the return value of M's _Generic structure, and is called with object and arguments.

_Generic isn't the only compile-time operator (e.g. the ternary operator should be compile-time as well when given constants), but it has three advantages: firstly, it guarantees the operation will not happen at runtime; secondly, it doesn't double-evaluate your THIS pointer (since the expression used for dispatch is not compiled); and thirdly, since the dummy type expressions are name-based, we don't need to waste effort computing enum IDs for methods, making sure they're consistent across class definitions... just paste the name and the selector works. (Note that you don't even have to declare the dummy types - it's implicit in the use, although doing so doesn't hurt.)

Fundamentally this is really just overloading, but it's oriented towards grouping the method definitions by class instead of by selector name, so there's still some element of OOP to it.

Working example:

#include <stdio.h>

// centralized list of classes
#define CLASSES  class1, class2

// static class dispatch
#define M(MSG, THIS, ...) _Generic((THIS), \
  class1 *: class1_selector((struct class1_ ## MSG ## _dmy*)0), \
  class2 *: class2_selector((struct class2_ ## MSG ## _dmy*)0), \
  default: "error: unknown class")(THIS, ## __VA_ARGS__)


// define class1
typedef struct {
    char* name;
    int   one;
} class1;
void class1_init( class1* self ) {
    self->name = "test";
    self->one=5;  
}
void class1_show(class1 * self) { printf("class1: (%s, %d)\n", self->name, self->one); }
int class1_getOne(class1 * self) { return self->one; }

// class1 static method dispatch table
#define class1_selector(MSG) _Generic((MSG), \
  struct class1_init_dmy *: class1_init, \
  struct class1_show_dmy *: class1_show, \
  struct class1_getOne_dmy *: class1_getOne, \
  default: "error: unknown method")


// define class2
typedef struct {
    int x;
} class2;
void class2_show(class2 * self) { printf("class2: (%d)\n", self->x); }
char* class2_tryme( class2* self, char* echo ) { return echo; }

// class2 static method dispatch table
#define class2_selector(MSG) _Generic((MSG), \
  struct class2_tryme_dmy *: class2_tryme, \
  struct class2_show_dmy *: class2_show, \
  default: "error: unknown method")


int main(void) {
    class1 a;
    class2 b;

    M( init, &a );
    b.x = 13;
    M( show, &a );
    M( show, &b );
}

Because I hate repetition and like excessive metaprogramming, here's a version that uses looping macros to eliminate most of the character overhead involved in defining classes (the block at the top should be hidden away in a different file; cmacros.h is implemented here):

#include <stdio.h>

// !!METAPROGRAMMING BOILERPLATE
#include "cmacros.h"
// static class dispatch
#define M(MSG, ...) _Generic(M_FIRST(__VA_ARGS__), \
  M_REST(M_REST(M_FOR_EACH(M_RE_EXP, \
    (D1, D2, D3) \
    M_ZIP_WITH(MSG_SEL, (CLASSES), M_ENLIST(MSG, M_NARGS(CLASSES))) ) )) \
  ,default: "error: unknown class") \
  (__VA_ARGS__)

#define M_RE_EXP(E) ,M_FIRST E*: _Generic(DUMMY_SEL(M_FIRST E, M_FIRST(M_REST E)), \
  M_CONC2(M, M_REST(M_REST E)) \
  default: "error: unknown method")

#define M_CONC2(L, R) M_CONC2_(L, R)
#define M_CONC2_(L, R) L##R

#define MSG_SEL(CLASS, MSG) ,MSG_SEL_(CLASS, MSG)
#define MSG_SEL_(CLASS, MSG) (CLASS, MSG, LIST_METHODS(CLASS, CLASS ## _methods))

#define DUMMY_SEL(CLASS, MSG) DUMMY_SEL_(CLASS, MSG)
#define DUMMY_SEL_(CLASS, MSG) (struct CLASS##_##MSG##_dmy*)0

#define LIST_METHODS(CLASS, ...) \
  _ZIP_WITH(METHOD_SEL, M_ENLIST(CLASS, M_NARGS(__VA_ARGS__)), (__VA_ARGS__))
#define METHOD_SEL(CLASS, METH) METHOD_SEL_(CLASS, METH)
#define METHOD_SEL_(CLASS, METH) struct CLASS##_##METH##_dmy*: CLASS##_##METH,
// !!END OF BOILERPLATE


// centralized list of classes
#define CLASSES  class1, class2


// define class1
typedef struct {
    char* name;
    int   one;
} class1;
void class1_init( class1* self ) {
    self->name = "test";
    self->one=5;  
}
void class1_show(class1 * self) { printf("class1: (%s, %d)\n", self->name, self->one); }
int class1_getOne(class1 * self) { return self->one; }

#define class1_methods init, show, getOne


// define class2
typedef struct {
    int x;
} class2;
void class2_show(class2 * self) { printf("class2: (%d)\n", self->x); }
char* class2_tryme( class2* self, char* echo ) { return echo; }

#define class2_methods show, tryme


int main(void) {
    class1 a;
    class2 b;

    M( init, &a );
    b.x = 13;
    M( show, &a );
    M( show, &b );
}

Finally, this last version shows the reason for swapping MSG and THIS in the definition of M - it makes it possible to eliminate warnings about unused variadic arguments without relying on a GCC extension. (Besides, who says you need to be controlled by C++'s obj.method convention?)

N.B. there is a possible downside to this strategy (who would have thought) - the macro step pastes the full method table selection for every class in at each method call site. There's no runtime code bloat because _Generic removes it all again, but it will probably slow compilation down, or most likely run out of compiler memory, if you have hundreds of classes and methods! Overloading would be much more efficient in this respect.

Community
  • 1
  • 1
Alex Celeste
  • 12,824
  • 10
  • 46
  • 89