惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

AI
AI
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Google DeepMind News
Google DeepMind News
T
Tenable Blog
博客园_首页
S
Securelist
Spread Privacy
Spread Privacy
Google Online Security Blog
Google Online Security Blog
Forbes - Security
Forbes - Security
Engineering at Meta
Engineering at Meta
U
Unit 42
L
LINUX DO - 热门话题
量子位
T
Threat Research - Cisco Blogs
博客园 - 【当耐特】
C
Cyber Attacks, Cyber Crime and Cyber Security
K
Kaspersky official blog
MyScale Blog
MyScale Blog
P
Proofpoint News Feed
The Last Watchdog
The Last Watchdog
Google DeepMind News
Google DeepMind News
GbyAI
GbyAI
Martin Fowler
Martin Fowler
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Security Latest
Security Latest
Scott Helme
Scott Helme
V
Vulnerabilities – Threatpost
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
I
InfoQ
Know Your Adversary
Know Your Adversary
Cisco Talos Blog
Cisco Talos Blog
The Register - Security
The Register - Security
T
The Blog of Author Tim Ferriss
aimingoo的专栏
aimingoo的专栏
V2EX - 技术
V2EX - 技术
T
Tailwind CSS Blog
月光博客
月光博客
Recent Announcements
Recent Announcements
G
Google Developers Blog
F
Full Disclosure
W
WeLiveSecurity
宝玉的分享
宝玉的分享
腾讯CDC
G
GRAHAM CLULEY
Vercel News
Vercel News
Simon Willison's Weblog
Simon Willison's Weblog
美团技术团队
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Help Net Security
Help Net Security

herrkaefer

"Vibe planning \u003e vibe coding" "Anything to Markdown" "Built anocus: anonymous commenting for static sites" "日记与小说 -- AI 续写小说欣赏" "Any-podcast: from newsletters to a podcast" "Made MicPipe: a simple voice input tool using ChatGPT dictation" "关于 Tools 和 Skills 的一点感想" "Realtime monitoring of ComEd hourly price" "Introducing SwiftEdgeTTS" "Thoughts on the philosophy of building AI-native apps" "jelly鼻屎" "等饭的人" "Use home assistant to motivate my kid to brush teeth" "Migrated Blog to Hugo and Cloudflare Pages" "Easy Aspen monitoring for Chicago parents" "Introducing HabitBuilder: A simple Telegram bot for habit tracking" "鼓捣" "Open folder or file with Sublime Text from Finder toolbar" "Python dev workflow on macOS" "Create new text file from Finder toolbar" "Uno reinvented for 3-year-old kids" "Uno变身儿童数字游戏" "自动转发Twitter到微博" "Handle annoying operations of objects in Realm DB" "Move Jekyll blog to Ubuntu VPS" "Introducing Mole" "Note taking without note taking app" "Deploy Python web application on Ubuntu server" "Setup Shadowsocks / VPN on Ubuntu Server" "Linode Notes - Basic Setup" "psycopgr Tutorial" "pgRouting Notes" "PostgreSQL Notes" "阿城三王" "这一年,这一把日子" "另一面的发现——读《坟》" "定理" "封面与腰封" "Google book下载" "lulu最新写真出炉" "The Big Bang Theory第三季" "自拍婚纱照1" "日全食" "期待动画片" "《麦兜响当当》动画电影主题曲" "转:饶毅--“杂志拜物教”:何时发Cell Nature Science 论文害你" "转:饶毅--提醒年轻人:何时SCI害你?" "西安" "3d打印机" "Dropbox" "刷牙" "贴几张照片" "6156167" "永久和凤凰" "老板的想法" "春" "原来奥巴马也是个朗读者" "应邀发Freeware List 2.0" "史上最能睡的淘宝老板" "至少出名的效果是达到了" "错怪了msn" "独立游戏节2009" "114" "馒头" "Crayon Physics Deluxe" "2008,2009" "盖章记" "小虎队附身许巍" "怎么给word文档加索引:排序问题" "怎么给word文档加索引Q\u0026amp;A" "怎么给Word文档加索引" "教我如何不疯掉" "二则" "P" "哦!报告" "蓝天" "萧翰" "lm" "故宫印象" "转:美国历任总统像" "time can kill itself" "嗯" "建议,只是建议哦" "奥地利行记3" "奥地利行记2" "奥地利行记1" "叶子" "GayBoy" "天使教你扔frisbie" "门徒因何面容愁?" "手机教堂" "丝竹管弦之盛" "残奥" "争座位" "秋意浅" "总理府" "流觞曲水" "映带左右引以为" "咚咚咚 续" "茂林修竹又有"
"CLASS Style Adapted for Embedded Systems"
"herrkaefer" · 2016-09-10 · via herrkaefer

CLASS C Coding style

CLASS is “C Language Style for Scalability” developed by Pieter Hintjens:

The C Language Style for Scalability (CLASS) defines a consistent style and organization for scalable C library and application code built on modern C compilers and operating systems. CLASS aims to collect industry best practice into one reusable standard.

CLASS is all about writing APIs. Types and functions are grouped by tasks. I have used this style to write C codes after reading the unfinished book “Scalable C”, and it makes writing C code a great fun.

Problem

Recently I am developing some algorithms in C targeted at embedded system where static memory is used instead of dynamic heap memory. So I tried to adapt the CLASS style to the embedded version.

To avoid use of heap memory, we need to re-consider the following issues:

  • How to create object with adjustable parameters?
  • How to initialize object with parameters?
  • How to return memory block in APIs?

Below is my initial solution.

CLASS for Embedded System (CLASSES)

I wrote a skeleton project for demonstration purpose. embedded-c-boilerplate. The following of this post is basically explanations of the codes. Let’s take a buffer class for example.

Instead of creating object dynamically with input parameters in new(), we turn to use macros to define parameters in advance.

For each class, use a separate myclass.ini file to allow the class user to define parameters to configure the object. These parameters are essential to object creation and initialization.

For example, in buffer.ini:

#define BUFFER_SIZE 128
#define BUFFER_PARAM_A 1
#define BUFFER_PARAM_B 9.4

You can comment any line to use the default value. Default values are defined in .h file (explained bellow).

Then include this file before the class header file.

This approach is invasive because if you change parameters recompiling is necessary. However, it is still helpful for separating configuration from code. You could change parameters without going deeply into the class implementations.

Parameter regularization

For each parameter essential to object creation and initialization:

  1. Define default parameter which can be overrided by user defined value in cfg file (the .ini file).
  2. Typecast it to inner representation prefixed by a underscore.
  3. Perform static assertion to verify that the value is within correct range.

These are done in the class header. For the buffer example, in buffer.h:

#include "buffer.ini"

#ifndef BUFFER_SIZE
#define BUFFER_SIZE 32
#endif

#define _BUFFER_SIZE (size_t) BUFFER_SIZE
ct_assert (_BUFFER_SIZE <= 1024);

#ifndef BUFFER_PARAM_A
#define BUFFER_PARAM_A 2
#endif

#define _BUFFER_PARAM_A (int) BUFFER_PARAM_A
ct_assert (_BUFFER_PARAM_A <= 3);

#ifndef BUFFER_PARAM_B
#define BUFFER_PARAM_B 7.0
#endif

#define _BUFFER_PARAM_B (double) BUFFER_PARAM_B
ct_assert (_BUFFER_PARAM_B < 10.0);

ct_assert macro does compile-time assertion of to verify that the parameter is within the correct range, which is defined as below:

#define ct_assert3(COND,MSG) typedef char static_assertion_failed_at_line_##MSG[(!!(COND))*2-1]
#define ct_assert2(COND,MSG) ct_assert3(COND,MSG)
#define ct_assert(COND) ct_assert2(COND,__LINE__)

From this step on, we will use parameters with inner representations (for structure defination and init(), see bellow), e.g._BUFFER_SIZE instead of BUFFER_SIZE.

Data structure defination

Structure defination has to be public in the header file, as for embedded application, object is instantiated statically, and the compiler needs to know its size.

This is the buffer_t structure:

typedef struct {
    double data[_BUFFER_SIZE];
    size_t size;
    int param_a;
    double param_b;
} buffer_t;

Note that buffer_t is a new defined struct type. To instantiate it, or define a variable:

Object initialization

Constructor new() and destructor free() or destroy() are no longer needed. Instead, a init() API is added for initializing object, e.g.

// Initialize buffer object
void buffer_init (buffer_t *self);

In init(), we

  • Use parameters with inner representation (e.g. _BUFFER_SIZE) to initialize corresponding properties.
  • Assign parameter to variable if you need to access it later. The idea is: using of parameter macros should stop after init(). In other APIs, we do not see the macros any more. Instead, we use the parameter variables. Of course, this approach will cost a little more memory than using macros directly.
  • For other properties, try to initialize them to default values (0/false/NULL)

init() should be called once before you use the object.

For example,

void buffer_init (buffer_t *self) {
    assert (self);
    memset (self, 0, _BUFFER_SIZE * sizeof (double));
    self->size = _BUFFER_SIZE;
    self->param_a = _BUFFER_PARAM_A;
    self->param_b = _BUFFER_PARAM_B;
}

APIs design guide

Naming conventions

This is the same as in CLASS style. Every API should accept the pointer of the object as the first parameter, followed by other parameters:

myclass_mymethod (myobject, ...)

Initialization

void myclass_init (myclass_t *self);

Constructor (new(), or create()) and destructor (free() or destroy()) are no longer needed. Instead we add a init() to do initilization works, which has been explained above.

Initialize the object once before you use it:

myclass myobject;
myclass_init (&myobject);

Getter and setter

Though you can manipulate properties directly as the structure is public, doing these via getter and setter APIs are recommended. You should pretend that the structure implementation is hidden, and never manipulate properties directly.

It is not necessary to write getter and setter for every property. For const properties, do not provide a setter.

Other APIs

APIs for real works, e.g.

void buffer_push (buffer_t *self, double value);

No heap memory allocation

  • Avoid using dynamic memory allocation, e.g. malloc() or alloc()
  • To return memory block, use inout parameter rather than memory block dynamically allocated inside the function.

For example, to calculate the squared root of every element in a buffer, we write a function as:

void buffer_sqrt (buffer_t *self, double *output) {
    assert (self);
    assert (output);
    for (size_t idx = 0; idx < self->size; idx++)
        output[idx] = sqrt (self->data[idx]);
}

It is better to indicate the inout parameter by name such as “output” in the API declaration, and note it in the comment.

// Squared root of buffer data.
// Return result in param output.
void buffer_sqrt (buffer_t *self, double *output);

Problems and limitations

Same class defination, multiple configuraton

For example, you need two buffers with different length in a program. It seems not easy to realize if the configuration is related to memory allocation, which is done during compiling stage. Buffers with differnt length should actually be viewed as instances of different classes.

Building and using libraries