优雅地尝试以特定的方式执行各种功能
Elegantly attempt to execute various functions a specific way
我尝试按顺序执行各种函数n次,只有在前一个函数没有返回false(错误)的情况下才能继续执行,否则我会重置并重新开始。
一个序列的例子是:
- 打开模块:
module.power(true)
,3次尝试 - 等待信号:
module.signal()
,10次尝试 - 发送消息:
module.sendSMS('test')
,3次尝试 - 关闭模块:
module.power(false)
,1次尝试
这些操作中的每一个都以相同的方式完成,只是更改DEBUG文本和要启动的函数:
DEBUG_PRINT("Powering ON"); // This line changes
uint8_t attempts = 0;
uint8_t max_attempts = 3; // max_attempts changes
while(!module.power(true) && attempts < max_attempts){ // This line changes
attempts++;
DEBUG_PRINT(".");
if(attempts == max_attempts) {
DEBUG_PRINTLN(" - Failed.");
soft_reset(); // Start all over again
}
delay(100);
}
DEBUG_PRINTLN(" - Success");
wdt_reset(); // Reset watchdog timer, ready for next action
有没有一种优雅的方法可以把这个过程放在一个函数中,我可以调用它以这种特定的方式执行所需的函数,例如:
void try_this_action(description, function, n_attempts)
这将使上面的操作1-4类似于:
try_this_action("Powering ON", module.power(true), 3);
try_this_action("Waiting for signal", module.signal(), 10);
try_this_action("Sending SMS", module.sendSMS('test'), 3);
try_this_action("Powering OFF", module.power(false), 1);
我遇到的一个困难是,被调用的函数有不同的语法(有些接受参数,有些不接受…)。除了在我需要的地方复制/粘贴大量代码之外,还有更优雅的模块化方法吗?
我遇到的一个困难是调用的函数有不同的语法(有些采用参数,有些不采用…)。
这确实是一个问题。同时,对于同一个函数,实际函数参数也可能发生变化。
有没有更优雅的除了复制/粘贴chunck代码之外,还有一种模块化的方法我需要它的地方?
我认为你可以制作一个可变函数,它使用函数的特定知识来调度,以处理不同的函数签名和实际参数。不过,我怀疑我是否会认为这个结果更优雅。
我倾向于通过宏来处理这份工作,而不是:
// desc: a descriptive string, evaluated once
// action: an expression to (re)try until it evaluates to true in boolean context
// attempts: the maximum number of times the action will be evaluated, itself evaluated once
#define try_this_action(desc, action, attempts) do {
int _attempts = (attempts);
DEBUG_PRINT(desc);
while(_attempts && !(action)) {
_attempts -= 1;
DEBUG_PRINT(".");
delay(100);
}
if (_attempts) {
DEBUG_PRINTLN(" - Success");
} else {
DEBUG_PRINTLN(" - Failed.");
soft_reset();
}
wdt_reset();
} while (0)
用法正如您所描述的:
try_this_action("Powering ON", module.power(true), 3);
等。。尽管效果就像你在每个位置插入了每个动作的代码,但使用这样的宏会产生更容易阅读的代码,而且在词汇上不会重复。因此,例如,如果您需要更改尝试操作的步骤,可以通过修改宏一次性完成。
您需要使函数指针都具有相同的签名。我会用这样的东西;
typedef int(*try_func)(void *arg);
并且具有类似于以下内容的try_this_action(...)
签名;
void try_this_action(char * msg, int max_trys, try_func func, void *arg)
然后你会实施类似的行动;
int power(void *pv)
{
int *p = pv;
int on_off = *p;
static int try = 0;
if (on_off && try++)
return 1;
return 0;
}
int signal(void *pv)
{
static int try = 0;
if (try++ > 6)
return 1;
return 0;
}
并这样称呼他们;
int main(int c, char *v[])
{
int on_off = 1;
try_this_action("Powering ON", 3, power, &on_off);
try_this_action("Signaling", 10, signal, 0);
}
不同arity的函数可以用通用签名来抽象(想想main
)。与其每个人都给出自己独特的论点,不如简单地为他们提供:
- 参数计数
- 指向参数的指针向量
这就是操作系统如何处理它运行的所有程序。我举了一个非常基本的例子,你可以在下面查看。
#include <stdio.h>
#include <stdlib.h>
/* Define total function count */
#define MAX_FUNC 2
/* Generic function signature */
typedef void (*func)(int, void **, const char *);
/* Function pointer array (NULL - initialized) */
func functions[MAX_FUNC];
/* Example function #1 */
void printName (int argc, void **argv, const char *desc) {
fprintf(stdout, "Running: %sn", desc);
if (argc != 1 || argv == NULL) {
fprintf(stderr, "Err in %s!n", desc);
return;
}
const char *name = (const char *)(argv[0]);
fprintf(stdout, "Name: %sn", name);
}
/* Example function #2 */
void printMax (int argc, void **argv, const char *desc) {
fprintf(stdout, "Running: %sn", desc);
if (argc != 2 || argv == NULL) {
fprintf(stderr, "Err in %s!n", desc);
return;
}
int *a = (int *)(argv[0]), *b = (int *)(argv[1]);
fprintf(stdout, "Max: %dn", (*a > *b) ? *a : *b);
}
int main (void) {
functions[0] = printName; // Set function #0
functions[1] = printMax; // Set function #1
int f_arg_count[2] = {1, 2}; // Function 0 takes 1 argument, function 1 takes 2.
const char *descs[2] = {"printName", "printMax"};
const char *name = "Natasi"; // Args of function 0
int a = 2, b = 3; // Args of function 1
int *args[2] = {&a, &b}; // Args of function 1 in an array.
void **f_args[2] = {(void **)(&name),
(void **)(&args)}; // All function args.
// Invoke all functions.
for (int i = 0; i < MAX_FUNC; i++) {
func f = functions[i];
const char *desc = descs[i];
int n = f_arg_count[i];
void **args = f_args[i];
f(n, args, desc);
}
return EXIT_SUCCESS;
}
您可以使用变元函数,在参数列表中首先声明那些始终存在的参数,然后声明变量部分。在下面的代码中,我们为操作函数定义了一种类型,void返回具有参数列表:
typedef void (*action)(va_list);
然后定义为动作执行做准备的通用动作例程:
void try_this_action(char *szActionName, int trials, action fn_action, ...)
{
va_list args;
va_start(args, fn_action); //Init the argument list
DEBUG_PRINT(szActionName); // This line changes
uint8_t attempts = 0;
uint8_t max_attempts = trials; // max_attempts changes
//Here we call our function through the pointer passed as argument
while (!fn_action(args) && attempts < max_attempts)
{ // This line changes
attempts++;
DEBUG_PRINT(".");
if (attempts == max_attempts)
{
DEBUG_PRINTLN(" - Failed.");
soft_reset(); // Start all over again
}
delay(100);
}
DEBUG_PRINTLN(" - Success");
wdt_reset(); // Reset watchdog timer, ready for next action
va_end(args);
}
必须对每个函数进行编码以使用参数列表:
int power(va_list args)
{
//First recover all our arguments using the va_arg macro
bool cond = va_arg(args, bool);
if (cond == true)
{
... //do something
return true;
}
return false;
}
用途为:
try_this_action("Powering ON", 3, module.power, true);
try_this_action("Waiting for signal", 10, module.signal);
try_this_action("Sending SMS", 3, module.sendSMS, "test");
try_this_action("Powering OFF", 1, module.power, false);
如果您需要更多关于可变函数和stdarg.h宏用法的信息,请在网上搜索。从这里开始https://en.cppreference.com/w/c/variadic.
它也可以被编码为宏实现,正如John Bollinger回答中的优秀建议,但在这种情况下,你必须考虑每个宏使用都会实例化整个代码,这最终可能会更好地提高速度(避免函数调用),但可能不适合内存有限的系统(嵌入式),或者需要引用函数try_this_action
(不存在)的位置。
- 程序按执行方式工作,直到我向其添加析构函数为止
- 在服务器上执行操作的正确和 REST 方式?
- 在Visual C ++中与Stockfish(外部可执行文件)通信的最佳方式
- 在 Windows 命令行中运行.exe在使用 2 种相同方式执行时会产生不同的输出
- 无法以正确的方式执行其他程序
- 执行随机开关函数的QT方式是什么连续两次使用相同情况的方法
- 优雅地尝试以特定的方式执行各种功能
- 如果 QApplication 执行延迟,QWebEngineView 在加载内容时会以静默方式失败
- 为什么程序没有以正确的方式执行
- 如果与未使用的库链接,可执行文件的构建方式是否不同?
- pi的倍数到千分之一的值可能会改变循环执行方式
- 优雅的方式来执行许多闹钟
- 可执行文件在被复制时以某种方式损坏
- 使用 Linux Eclipse,我可以以编程方式判断我正在调试器 (gdb) 中执行
- 为什么 GNU ld 在链接可执行文件和共享对象时以不同的方式解析符号
- Visual Studio-在逐步执行时查看堆栈大小增长的方式
- 使用C++以编程方式执行静默安装
- 如何在没有弹出cmd窗口的情况下以c ++静默方式执行shell程序
- 给定一个期货容器,如何以非阻塞的方式执行所有获取
- 有没有办法在C++中以编程方式执行 adb 命令?这C++部分代码是使用 android Studio 中的 ndk b