Reputation: 77
I'm writing a firmware in which I would like to define functions "command" in multiple .c files, and automatically insert them in a list of function pointers. Subsequently from the serial port I want to recall one of these commands present in the array, and execute it with a list of arguments.
There is a firmware for 3d printers, on which I am relying, where such a thing is implemented.
https://github.com/KevinOConnor/klipper
The software structure used is this "remote procedure call" https://en.wikipedia.org/wiki/Remote_procedure_call
In this firmware the commands are implemented in the .c h file and there is no prototype in the .h
For example:
void command_set_digital_out(uint32_t *args)
{
gpio_out_setup(args[0], args[1]);
}
DECL_COMMAND(command_set_digital_out, "set_digital_out pin=%u value=%c");
below is a list of the definitions used
// Declare a function to run when the specified command is received
#define DECL_COMMAND_FLAGS(FUNC, FLAGS, MSG) \
DECL_CTR("DECL_COMMAND_FLAGS " __stringify(FUNC) " " \
__stringify(FLAGS) " " MSG)
#define DECL_COMMAND(FUNC, MSG) \
DECL_COMMAND_FLAGS(FUNC, 0, MSG)
// Declare a compile time request
#define DECL_CTR(REQUEST) \
static char __PASTE(_DECLS_, __LINE__)[] __attribute__((used)) \
__section(".compile_time_request") = (REQUEST)
#define __stringify_1(x) #x
#define __stringify(x) __stringify_1(x)
#define ___PASTE(a,b) a##b
#define __PASTE(a,b) ___PASTE(a,b)
Subsequently when the firmware acquires serial code it is able to recall these declared functions, as requested. How is it possible ?
I am not able to replicate this, in my firmware written for AVR in platformio, is there an easy and similar way to declare a list of functions dynamically?
Upvotes: 0
Views: 360
Reputation: 77
I tried to look for an already existing solution to implement this software structure "remote procedure call", and I found this library for arduino : https://simplerpc.readthedocs.io/en/latest/
In this library I can define the communication interface and the functions to be remote.
My need is to command this firmware from C# !
The problem is that this library is written and compiled with .tcc, and this is my first time seeing this extension.
if i fail to use this library in tcc, anyone know similar libraries?
Upvotes: 0
Reputation: 141633
I should say I do not like at all the idea of "auto declare functions in an array". Placing data in sections are neat tricks, but it's just spagetti code - different parts of code are connected via invisible links - which may lead to unmaintainable mess. Instead of resorting to compiler tricks, I suggest to just write a single big array in one place with clear and simple and readable and maintainable code.
is there an easy and similar way to declare a list of functions dynamically?
That's relatively very very simple. You place data formatted how you want into a section. Gcc compiler picks up all sections from all files and concatenates them (let's say in random order) and generates to symbols - __start_SECTIONAME
and __end_SECTIONAME
that you can use to iterate over the elements in the section.
The following code:
// autofuncs.h ----------------------------------------------------------------------
// a header for the funcionality
#include <errno.h>
#include <string.h>
#include <stddef.h>
#include <assert.h>
#include <stdio.h>
// we'll only need name and a function pointer
struct autofunc_s {
const char *name;
int (*main)(int argc, char **argv);
};
// add struct autofunc_s element in section named autofunc
#define AUTOFUNC_ADD(name, func) \
__attribute__((__section__("autofunc"))) \
const struct autofunc_s _autofunc_##func = { name, func };
// a simple runner
// find a function named argv[0] and execute it
static int autofunc_exec(int argc, char **argv) {
extern const struct autofunc_s __start_autofunc;
extern const struct autofunc_s __stop_autofunc;
const struct autofunc_s *it = &__start_autofunc;
const size_t count = &__stop_autofunc - &__start_autofunc;
if (argc) {
for (size_t i = 0; i < count; ++i) {
if (strcmp(it[i].name, argv[0]) == 0) {
assert(it[i].main != NULL);
return it[i].main(argc, argv);
}
}
}
printf("Command not found\n");
return ENOENT;
}
// echo.c ----------------------------------------------------------------------
#include "autofuncs.h"
#include <stdio.h>
// example function to run
static int echo(int argc, char **argv) {
for (int i = 1; i < argc; ++i) {
printf("%s%s", argv[i], i + 1 == argc ? "\n" : " ");
}
return 0;
}
AUTOFUNC_ADD("echo", echo)
// main.c ----------------------------------------------------------------------
// a very very short main that shows the funcionality
#include "autofuncs.h"
int main(int argc, char **argv) {
// You would possibly here read one line from serial device
// and tokenize the line on whitespaces here in some loop
argc--; argv++;
return autofunc_exec(argc, argv);
}
then compile and link:
$ gcc main.c echo.c
and you can:
$ ./a.out something
Command not found
$ ./a.out echo 1 2 3
1 2 3
The same way instead of using __attribute__((__section__("autofunc")))
you could make a big array of structures that has all the information in one place.
Many compilers have different syntax of how to place data in a section and how then get to that section. Research your compiler documentation.
Note that that is some convention that sections with leading dot are rather reserved. Prefer to use sections without leading dot for custom sections.
How is it possible ?
Compiler and linker expose a specific interface to work with user defined section that allows to do just that.
Upvotes: 1