优雅地尝试以特定方式执行各种功能

时间:2019-02-06 13:42:44

标签: c++ arduino

我试图连续执行n次各种功能,只有在前一个功能没有返回false(错误)的情况下才向前移动,否则我将重置并重新开始。

一个序列的例子是:

  1. 打开模块:module.signal(),尝试3次
  2. 等待信号:module.sendSMS('test'),尝试10次
  3. 发送一条消息:module.power(false),尝试3次
  4. 关闭模块: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 ,尝试1次

所有这些操作都以相同的方式进行,只是更改了DEBUG文本和要启动的功能:

void try_this_action(description, function, n_attempts)

有没有一种优雅的方法可以将这个过程放入一个函数中,我可以调用该函数以特定的方式执行所需的函数,例如:

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);

会像上面那样执行1-4的动作:

{{1}}

我遇到的一个困难是调用的函数具有不同的语法(有些采用参数,有些则没有...)。除了在需要的地方复制/粘贴大量代码之外,还有其他更优雅的可调制方式吗?

4 个答案:

答案 0 :(得分:1)

  

我遇到的一个困难是调用的函数具有不同的语法   (有些采用参数,有些则没有...)。

这确实是一个问题。伴随着它,您可能会为同一函数改变实际函数参数。

  

有没有更优雅的   除了复制/粘贴代码块之外,这种方法的可调制方式   我需要的任何地方?

我认为您可以制作一个可变参数函数,该函数使用要分配的特定函数知识来处理不同的函数签名和实际参数。不过,我怀疑我会认为结果更优雅。

我倾向于通过宏来代替这项工作:

// 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);

etc ..尽管效果就像您确实在每个位置插入每个动作的代码,但是使用这样的宏将产生易于阅读的代码,即在词汇上不是重复的。因此,例如,如果您需要更改尝试操作的步骤,则可以通过修改宏来一次全部完成操作。

答案 1 :(得分:0)

您需要使所有函数指针具有相同的签名。我会用这样的东西;

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);
}

答案 2 :(得分:0)

可以使用通用签名来抽象不同功能的功能(请考虑main)。您不必为每个参数都提供唯一的参数,而只需为它们提供:

  1. 参数计数。
  2. 指向参数的指针的向量。

这是操作系统如何处理所有仍在运行的程序的方式。我给出了一个非常基本的示例,您可以在下面查看。

#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: %s\n", desc);
    if (argc != 1 || argv == NULL) {
        fprintf(stderr, "Err in %s!\n", desc);
        return;
    }
    const char *name = (const char *)(argv[0]);
    fprintf(stdout, "Name: %s\n", name);
}

/* Example function #2 */
void printMax (int argc, void **argv, const char *desc) {
    fprintf(stdout, "Running: %s\n", 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: %d\n", (*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;
}

答案 3 :(得分:0)

您可以使用可变参数函数,首先在参数列表中声明始终存在的那些参数,然后声明变量部分。 在下面的代码中,我们为动作函数定义了一个类型,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(毫无意义)的系统。