使用C、gcc、C99和宏优化微控制器的最低限度OOP 通常我必须在C中编写微控制器,因为C++编译器经常不可用,或者由于各种错误不能生成非常小的代码。但通常,OOP“语法糖”在使硬件程序更清晰地封装以便于维护时非常方便;因此,我想知道是否有一种方法可以在C中使用OOP语法,在这种方法中,可以尽可能多地利用OOP开销(在不需要时)以可移植的方式进行优化。例如:如果gcc不适用于不同的微控制器,那么可以针对不同的微控制器使用gcc进行优化,或者使用gcc的预处理器和通用ANSI-C编译器进行优化
我发现只有这样的线程,它们通常通过在结构中嵌入指针来执行OOP,但这并不总是我想要的,因为当我对虚拟方法或类似的东西不感兴趣时,它会浪费内存。我可以在需要这些特性的链接中始终遵循编码风格,但我想开发不需要这些特性的技术;例如,我只想用OOP范式编程,用简单易懂的代码(不一定是C++,虽然我喜欢C++),并且当一些OOP范式不使用时,仍然能够实现最小的C程序内存使用。 因此,我尝试使用gcc和C99,因为一般来说,gcc 3.2或更高版本适用于大多数平台;并意识到我可以使用C99中的sizeof()和typeof()编译器函数从未使用/未初始化的联合成员(因此类必须是具有子结构的联合)自动索引类(这是一种“技巧”),以便访问宏创建的编译时常量查找表,该表可以绑定数据和方法,并保证所有类型的检查。等等等等等等 例如:GCC允许对常量结构和数组进行优化,当它们的成员仅作为常量表达式访问时,所以我想我可以使用它来构建一个基于宏的编译时绑定系统,其中OOP开销在GCC中处理,并实际优化出最终的二进制 有了这个系统,我现在可以进行可变的宏方法调用,比如:M(a,init,“With”,“any”,“parameters”,7),它使用可变数量的参数查找变量a的类型,调用method init 请参阅下面的代码示例,并尝试使用它们——这比解释更简单:使用gcc-E查看宏扩展,注意对于仅限于ANSI的编译器,typeof()运算符必须替换为(void*)类型转换;类型检查仅适用于GCC 代码可以剪切和粘贴到文本编辑器中,文件名在第一行,它可以在普通PC系统上编译和运行 虽然我成功地消除了每个结构中的单个指针,以“指向”类的方法列表,从而在内存有限的微控制器中节省了内存,但我还是不太明白如何让编译器优化未使用的方法指针,因为我不得不使用(void*)类在数组中保存它们的指针,这些指针需要内存地址(结构的地址)和链接器实例;不要优化了 因此:我想知道是否有人知道一种改进我的解决方案的方法,即制作某种初始化的方法结构,在编译后进行优化(没有链接器地址),例如:当它的成员在代码中仅作为常量表达式访问时。本质上,我需要能够在数组中查找一个元素,其中每个数组元素的初始化部分是不同的classXXX\u mt,而不是classXXX\u mt所有类型转换为(void*)的地址列表 如果有人能想出一个简单的解决方案,我还想得到另外两个改进;cpp(c预处理器)不允许通过令牌连接从以前的宏中定义新宏(据我所知),因此我必须创建固定长度的宏列表(在我的示例中最多10个)来保存类定义;这意味着我在一个项目中最多只能上10节课;但理想情况下,我希望有一种方法使我的代码更通用,这样cpp就可以动态创建可变长度的列表。问题与c预处理器无法自动“计数”有关 其次,当我尝试为较新版本的GCC使用匿名结构时,我可能会通过从类联合定义中删除“m”名称并使用GCC-std=c11编译,从而消除访问ISO-C中成员数据所需的额外“m”,例如:foo.m.mydata,然后它只会给我错误,声称该结构未定义任何内容。。。因此,联合体内部的匿名结构即使在GCC4.8中也不起作用,尽管它应该起作用;如何使匿名结构正常工作 下面是我如何测试和实现一个包含文件voidbind.h的示例,该文件构建一个类列表,并将方法静态链接到该类类型的变量 最终,系统允许我像这个例子一样编程;我用gcc 4.0到4.9编译了它,没有问题:使用C、gcc、C99和宏优化微控制器的最低限度OOP 通常我必须在C中编写微控制器,因为C++编译器经常不可用,或者由于各种错误不能生成非常小的代码。但通常,OOP“语法糖”在使硬件程序更清晰地封装以便于维护时非常方便;因此,我想知道是否有一种方法可以在C中使用OOP语法,在这种方法中,可以尽可能多地利用OOP开销(在不需要时)以可移植的方式进行优化。例如:如果gcc不适用于不同的微控制器,那么可以针对不同的微控制器使用gcc进行优化,或者使用gcc的预处理器和通用ANSI-C编译器进行优化,c,oop,pointers,gcc,C,Oop,Pointers,Gcc,我发现只有这样的线程,它们通常通过在结构中嵌入指针来执行OOP,但这并不总是我想要的,因为当我对虚拟方法或类似的东西不感兴趣时,它会浪费内存。我可以在需要这些特性的链接中始终遵循编码风格,但我想开发不需要这些特性的技术;例如,我只想用OOP范式编程,用简单易懂的代码(不一定是C++,虽然我喜欢C++),并且当一些OOP范式不使用时,仍然能够实现最小的C程序内存使用。 因此,我尝试使用gcc和C99,因为一般来说,gcc 3.2或更高版本适用于大多数平台;并意识到我可以使用C99中的sizeof(
//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
}
一般来说,你要的是C++。你所发表的例子最有可能是使用C++编译器的效率更高或效率更高。
经常在嵌入式目标上有很多过时的版本,代码< > GCC < /C> >为C++生成坏代码,或者不支持所有的GRO C++细节。 您可以尝试运行<代码> ${YuyAcgRyPrime}-g++-NoSTDLIB——NOSTDICN< /COD>,这将使解析器中的C++语法不浪费所有空间。如果要禁用其他功能,可以使用remove runtime type checking and exception support添加
-fno rtti-fno exceptions
(请参阅)
<>因为C++分析器是C前端的一部分,即使C++没有被你的微控制器供应商官方支持,这仍然可能有效。
//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
// 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 //#}
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);
$ 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
#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" ) );
}
#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)
};
#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__)
#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")
#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 );
}
#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 );
}