- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我经常不得不用C语言对微控制器进行编程,因为C ++编译器通常不可用,或者由于各种错误而无法编写非常小的代码。但是,在使硬件程序更清晰地封装以便于维护时,OOP通常很方便。因此,我想找出是否有一种方法可以在C语言中执行OOP语法,在该方法中,可以尽可能多地使用OOP开销(不需要时)来以可移植的方式进行优化。例如:如果针对不同微控制器的gcc进行了优化,或者如果gcc对于该微控制器不可用,则可以使用gcc的预处理器和通用ANSI-C编译器进行优化。
我发现只有这样的线程,Elegant way to emulate 'this' pointer when doing OOP in C?通常通过将指针嵌入结构中来进行OOP,但这并不总是我想要的,因为当我对虚拟方法或类似方法不感兴趣时,它会浪费内存。我总是可以在需要这些功能的链接中遵循编码风格,但是我想开发不需要这些功能的技术。例如我只希望能够使用简单易懂的代码(虽然我喜欢C ++,不一定是C ++)使用OOP范例进行编程,并且在不使用某些OOP范例时仍能够实现最少的C程序内存使用。
因此,我求助于gcc和C99,因为一般而言,大多数平台都可以使用gcc 3.2或更高版本。并意识到我可以使用C99中的sizeof()和typeof()编译器函数从一个未使用/未初始化的联合成员(因此,类必须是具有子结构的联合)中自动索引类(一种“技巧”)。为了访问由宏创建的编译时常数查找表,该表可以绑定数据和方法,并保证所有类型检查。等等等等等等
例如:当仅以常量表达式形式访问其成员时,GCC允许对const结构和数组进行优化,因此我认为我可能能够使用它来构建基于宏的编译时绑定系统,从而在其中处理OOP开销GCC并实际上从最终二进制文件中进行了优化。
有了这个系统,我现在可以执行可变参数宏方法调用,例如:M(a,init,“ with”,“ any”,“ parameters ,, 7),它使用变量的数量查找变量a的类型,调用方法init。参数...
请参见下面的代码示例,并进行尝试-比解释要简单:使用gcc -E查看宏扩展,并注意仅适用于ANSI的编译器,typeof()运算符必须替换为(void *)类型转换类型检查仅适用于GCC。
该代码可剪切并粘贴到文本编辑器中,第一行带有文件名,并且可以在常规PC系统上编译并运行。
尽管我确实成功地摆脱了每个结构中的各个指针,以“返回”类的方法列表,从而在有限的内存微控制器中节省了内存,但我仍然无法弄清楚如何让编译器进行优化淘汰未使用的方法指针,因为我必须为类使用(void *)指针才能将它们保存在数组中,并且这些指针需要内存地址(结构的地址)和链接器实例;并且不要优化。
所以:我想知道是否有人知道通过某种初始化方法结构来改进我的解决方案的方法,该方法在编译后会优化(没有链接器地址),例如:当它的成员仅作为常量表达式访问时码。本质上,我需要能够在数组中查找一个元素,其中每个数组元素的初始化部分是不同的classXXX_mt,而不是所有类型转换为(void *)的classXXX_mt的地址列表。
如果有人能想到一个简单的解决方案,我还希望提供另外两项改进; cpp(c预处理器)不允许通过令牌级联从前一个宏中定义新宏(据我所知),因此我必须制作固定长度的宏列表(本例中最多为10个) )保存类的定义;这意味着一个程序中最多只能有10个课程;但理想情况下,我希望有一种使我的代码更通用的方法,以便cpp可以动态创建可变长度列表。例如:问题与c预处理器无法自动“计数”有关。
其次,当我尝试对较新版本的GCC使用匿名结构时,我可能会删除“ m”来摆脱访问ISO-C中的成员数据所需的额外“ m”,例如:foo.m.mydata从类并集定义中命名,并使用gcc -std = c11进行编译,然后它给了我错误,声称该结构未定义...因此,并集内的匿名结构即使在GCC 4.8中也不起作用;我怎样才能使匿名结构起作用?
下面是我如何测试和实现包含文件voidbind.h的示例,该文件生成类列表并将这些方法静态链接到该类类型的变量。
最终,该系统允许我像此示例一样进行编程。我用gcc 4.0到4.9编译时没有问题:
//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
}
//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 //#}
最佳答案
通常,您要的是C ++。您发布的示例很可能会使用C ++编译器更高效或更有效。
通常,在嵌入式目标上,您已经使用了过时的gcc
版本,这些版本会为c ++生成错误代码,或者不支持所有繁琐的c ++详细信息。
您可以尝试运行${your_arch_prefix}-g++ --nostdlib --nostdinc
,它将在解析器中启用c ++语法,而不会浪费所有空间。如果要禁用其他功能,则可以添加-fno-rtti -fno-exceptions
以及删除运行时类型检查和异常支持(请参见this question)。
由于C ++解析器是C前端的一部分,即使您的微控制器供应商未正式支持C ++,它也可能仍然有效(有时您也可以尝试自己编译供应商特定的版本并添加c ++到配置脚本中的语言)。
通常认为,这比尝试发明自己的OOP(例如宏DSL(特定于域的语言))要好。
这就是说,如果您不想走这条路并且不想使用手工vtable(如your link中所述)。最简单的事情是拥有编码约定。如果您不想多态,下面的代码就足够了。您可以在.c
文件中定义结构和函数,并将声明放在标头中。下面的函数可以直接调用,因此不在vtable中,并且第一个成员是c ++中的this
指针。 struct impl
是对象不包含vtable或类似内容的实际数据。
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);
container_of
宏,了解
media_entity_to_video_device强制转换的工作方式。 (如果这对您来说太少了,请看这本书:
Linux Device Drivers (LDD3))。
gcc -E
时调试代码,才能看到您的创建物从预处理器扩展以了解它们的含义。实际上在打电话。
$ 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
关于c - 通过优化使用C,gcc,C99和Macros改善微 Controller 的简约OOP,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29909665/
我正在尝试在 Conda 环境中编译一些代码,在那里我 之前安装的编译包gcc_linux-64 . 然而,即使在停用和重新激活环境之后,gcc还在/usr/bin/gcc . 我该怎么做才能让 Co
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 7 年前。 Improve
这其实是两个问题: 1 - 在我的 debian amd64 系统上,我似乎无法构建与 gmp/mpfr/mpc 动态链接的交叉 GCC。即使我删除 --disable-shared,它也总是静态链接
研究ELF格式的结果,可以看到目标文件中有一个符号对应每个函数,对应的符号表项的值为st_size,表示大小的功能。 问题是,即使我更改了目标文件中特定函数的 st_size 并链接了它,但可执行文件
海湾合作委员会的 documentation for #line directives说他们是这样的: #line "myfile.cpp" 123 但是当我用 g++ 5.1 检查输出时,它们实际上
我正在使用 as 和 gcc 来汇编和创建 ARM 汇编程序的可执行文件,正如 this 所推荐的那样教程,如下: 给定一个汇编源文件,program.s,我运行: as -o program.o p
long long x; double n; x=long long(n); 这不起作用。什么是正确的方法? 最佳答案 显而易见的: x = (long long) n; 关于gcc - 转换为长长
我想知道用于 gcc 的原子内置函数的头文件是什么? 我想使用这 2 个函数为我当前创建的线程库实现互斥锁。 bool __sync_bool_compare_and_swap (type *ptr,
它出现在 another question :gcc调用的程序和部件是什么? (特别是在编译 C 或 C++ 时)以便有人可以设计一些拦截和更改流程的方案以用于各种自定义编码目的? 最佳答案 编译器二
可能吗?我想使用 gcc喜欢 assembler并在将其编译为 ubuntu 上的可执行文件后。 我尝试过这个: gcc a.asm -o out.o 来自 out.o文件编译成.out可执行文件。
我写了一个简单的 C 程序 test.c : #include #include int add(int a, int b); int main() { int i=5,j=10;
即。所以如果你使用任何八进制文字,它会给你一个警告。 微软编译器的同样问题。 如果没有,是否有任何其他工具可以检测八进制文字。 (vim 似乎有一个很酷的技巧,它突出了第一个领先的将不同的颜色归零,但
我在旧线程中搜索。但没有找到任何线程回答我的问题。 gcc 是否像 vc++ 一样支持函数级链接? 如果是,我应该提供什么选项来链接目标文件和库? 最佳答案 看起来 gcc 不直接支持函数级链接。您可
也许标题并没有把问题说得那么准确:我知道当我运行 gcc foo.c 时,GCC 会调用其他为它完成所有工作的子程序,从而生成主 gcc 程序只是一个界面。但这究竟是如何完成的呢? 它是否使用syst
我听说最近版本的 gcc 非常擅长将通过函数指针的调用转换为直接调用。但是,我在网上或快速浏览 gcc 的源代码上找不到任何关于它的信息。有谁知道这是否真的是真的,如果是这样,它使用什么算法来做到这一
gcc/g++ 链接器选项“-Map”生成的“.map”文件用于什么? 以及如何阅读它们? 最佳答案 我建议为您投入生产的任何软件生成一个映射文件并保留一份副本。 它可用于破译崩溃报告。根据系统的不同
gcc信息文件在有关x86-64特定标志的部分中说 其他事情: There is no `-march=generic' option because `-march' ind
我想知道 gcc 链接器选项(例如:-Wl,options)是否可以更改编译后的可执行文件中的汇编指令,因为如果您使用某些 gcc 优化选项会发生这种情况? 当您比较编译后的二进制文件(例如比较签名)
是否有GCC编译指示会停止,暂停或中止编译过程? 我正在使用gcc 4.1,但也希望在gcc 3.x版本上也可以使用该编译指示。 最佳答案 您可能需要#error: edd@ron:/tmp$ g++
当我使用gcc编译C程序时我通常使用 -g 将一些调试信息放入 elf 文件中这样 gdb 就可以在需要时帮助我。 但是,我注意到有些程序使用 -ggdb,因为它应该使调试信息对 gdb 更加友好。
我是一名优秀的程序员,十分优秀!