проблема с объявлением массива указателей на функцию typedef

array pointer typedef

Спасибо за все ваши отзывы. Это помогло.

Я переписал это еще раз, и на этот раз все работает идеально. Я включил три варианта: простой пример с использованием массива указателей на функции, версию Lambda и сложную версию, аналогичную той, с которой я начал.

#define TASK_COUNT          3

typedef void ( *taskPointer ) ( );

// Простая версия:
void taskS1 () { Serial.println ( ( char* )"SIMPLE TASK 1" ); }
void taskS2 () { Serial.println ( ( char* )"SIMPLE TASK 2" ); }
void taskS3 () { Serial.println ( ( char* )"SIMPLE TASK 3" ); }

// Массив указателей на задачи
taskPointer callSimpleTask [ ] = {
taskS1,
taskS2,
taskS3,
};

// Запускаем простой тест
void showSimpleVersion () {
    Serial.println ( F ( "\nSimple Version:" ) );
    for (uint8_t i = 0; i < TASK_COUNT; i++) {
        callSimpleTask [ i ] ();
    }

}

// Версия Lambda: нет отдельных определений задач
void ( *callLambdaTask [ ] ) ( ) = {
    [ ] {Serial.println ( ( char* )"LAMBDA TASK 1" ); },
    [ ] {Serial.println ( ( char* )"LAMBDA TASK 2" ); },
    [ ] {Serial.println ( ( char* )"LAMBDA TASK 3" ); },
};

// Запускаем лямбда-тест
void showLambdaVersion () {
    Serial.println ( F ( "\nLambda Version:" ) );
    for (uint8_t i = 0; i < TASK_COUNT; i++) {
        callLambdaTask [ i ] ();
    }

}

// Сложная версия: каждый элемент массива представляет собой структуру, содержащую
// указатель на соответствующую задачу и связанные свойства
struct complexTaskDefinition {
    taskPointer task;
    bool repeat;                // Повторить мигание после задержки
    int32_t iterations;         // # раз повторить... -1 = навсегда
    uint32_t delay;             // MS ждать перед выполнением
};

// Создаем указатель на определение задачи
complexTaskDefinition *task;

// Доказать, что вызываемая функция может вызывать другие функции:
void taskC1 () { showTaskC1(); }
void taskC2 () { showTaskC1 (); }
void taskC3 () { showTaskC1 (); }

void showTaskC1 () {
    Serial.println ( ( char* )"COMPLEX TASK 1" );
}
void showTaskC2 () {
    Serial.println ( ( char* )"COMPLEX TASK 2" );
}
void showTaskC3 () {
    Serial.println ( ( char* )"COMPLEX TASK 3" );
}

complexTaskDefinition tasks [ ] [4] = {
    {taskC1, true, -1, 1000 },  // taskC1 повторяется вечно каждую секунду
    {taskC2, true, 10, 2000 },  // taskC2 выполняется 10 раз с паузой в 2 секунды между итерациями
    {taskC3, false, 3, 3000 },  // taskC3 запускается 3 раза с 3-секундными задержками между каждым, затем умирает
};

void showComplexTaskList () {
    Serial.println ( F ( "\nComplex Task List:" ) );
    for (uint8_t i = 0; i < TASK_COUNT; i++) {
        task = tasks [ i ];
        Serial.print ( '[' );
        Serial.print ( i );
        Serial.print ( F ( "] REPEAT:" ));
        Serial.print ( ( task->repeat ) ? "YES" : "NO" );
        Serial.print ( F ( ", DELAY:" ) );
        Serial.print ( task->delay );
        Serial.print ( F ( ", ITERATIONS:" ) );
        Serial.print ( task->iterations );
        Serial.println ();
    }
}

void showComplexVersion () {
    Serial.println ( F ( "\nComplex Version:" ) );
    for (uint8_t i = 0; i < TASK_COUNT; i++) {
        task = tasks [ i ];
        task->task ();
// (( *tasks ) [ i ]).task ();
    }
}

void setup () {
    Serial.begin ( 115200 );
    while (!Serial.availableForWrite ()) {}

    showSimpleVersion ();

    showLambdaVersion ();

    showComplexVersion ();

    showComplexTaskList ();
}

void loop () {
}

Вот результат: Простая версия:

SIMPLE TASK 1
SIMPLE TASK 2
SIMPLE TASK 3

Lambda Version:
LAMBDA TASK 1
LAMBDA TASK 2
LAMBDA TASK 3

Complex Version:
COMPLEX TASK 1
COMPLEX TASK 1
COMPLEX TASK 1

Complex Task List:
[0] REPEAT:YES, DELAY:1000, ITERATIONS:-1
[1] REPEAT:YES, DELAY:2000, ITERATIONS:10
[2] REPEAT:NO, DELAY:3000, ITERATIONS:3

Я хочу провести еще один эксперимент, на этот раз со сложной версией в качестве класса.

, 👍0

Обсуждение

цикл() и настройка()?, @Juraj

Какую ошибку компилятора вы получаете?, @Edgar Bonet

Плата? Версия ардуино IDE? Он может быть скомпилирован без каких-либо предупреждений/ошибок для платы на базе AVR. Кстати: &task1, &task2, &task3 более правильно., @KIIV

@KIIV Начиная с C99, & совершенно необязателен для указателей функций. Ни одно из них не является «более правильным», чем другое, они оба полностью правильны., @Majenko

@majenko на C да. В C++ также есть случаи, когда это необходимо (например, указатели на функции-члены). Правильнее имелось в виду "да, я хочу указатель на функцию, я не забыл ()" - показывая ясное намерение, @KIIV

Я не понимаю, почему вы задали вопрос без сообщения об ошибке и той части скетча, где возникает ошибка, и только добавили их позже, @Juraj


1 ответ


1

Попробуйте заменить

taskDefinition _tasks [ ] [4] = {

от

taskDefinition _tasks [ ] = {

Короче говоря, вы определили _tasks как двухмерный массив объектов (массив массивов объектов), в то время как вы собираетесь использовать его как одномерный массив.


Обратите внимание, что сообщение об ошибке довольно явное, если вы потратите время на разобрать:

запрос задачи участника в _tasks[((int)i)]',

Вы запрашиваете _tasks[i].task,

который не относится к классу

но _tasks[i] не является объектом.

введите 'taskDefinition [4]'

Вместо этого это массив из четырех объектов taskDefinition.


Изменить: исправление ответа в соответствии с последней версией вопроса.

Ваш код работает, но не совсем так, как вы ожидаете. Вы писали:

complexTaskDefinition tasks [ ] [4] = ...;

Здесь вы определяете 2D-массив объектов, в то время как вы намеревались определить одномерный массив (почему мне кажется, что я повторяю себя?).

complexTaskDefinition tasks [ ] [4] = {
    {taskC1, true, -1, 1000 },
    {taskC2, true, 10, 2000 },
    {taskC3, false, 3, 3000 },
};

Это определение, хотя и синтаксически правильное, ужасно дурацкое. Ты определяют tasks как массив из 3 элементов. Каждый предмет сам по себе массив из 4 объектов, где первый объект массива явно инициализируется, а три других неявно инициализируются все биты-ноль. Так что в основном ваше определение эквивалентно этому явная версия:

complexTaskDefinition tasks [3] [4] = {
    {{taskC1, true, -1, 1000 }, {nullptr, false, 0, 0},
     {nullptr, false, 0, 0},    {nullptr, false, 0, 0}},
    {{taskC2, true, 10, 2000 }, {nullptr, false, 0, 0},
     {nullptr, false, 0, 0},    {nullptr, false, 0, 0}},
    {{taskC3, false, 3, 3000 }, {nullptr, false, 0, 0},
     {nullptr, false, 0, 0},    {nullptr, false, 0, 0}}
};

Тогда вы написали:

task = tasks [ i ];

Здесь tasks[i] — это строка двумерного массива, т. е. одномерный массив из 4 объектов, тогда как task является указателем на объект. Уступка законна, потому что здесь, как и в большинстве контекстов, массив «распадается» до указателя на его первый элемент. Приведенная выше строка эквивалентна явной версии:

task = &tasks[i][0];

К счастью, это указывает на один из элементов массив, который был явно инициализирован!

В качестве иллюстрации вышеизложенного приведена модифицированная версия showComplexTaskList(), который перебирает все задачи в массиве:

void showComplexTaskList () {
    Serial.println ( F ( "\nComplex Task List:" ) );
    Serial.print(F("number of tasks: "));
    Serial.println(sizeof tasks / sizeof(complexTaskDefinition));
    for (uint8_t i = 0; i < TASK_COUNT; i++) {
        for (uint8_t j = 0; j < 4; j++) {
            task = &tasks[i][j];
            Serial.print ( '[' );
            Serial.print ( i );
            Serial.print ( F ("]["));
            Serial.print ( j );
            Serial.print ( F ( "] REPEAT:" ));
            Serial.print ( ( task->repeat ) ? "YES" : "NO" );
            Serial.print ( F ( ", DELAY:" ) );
            Serial.print ( task->delay );
            Serial.print ( F ( ", ITERATIONS:" ) );
            Serial.print ( task->iterations );
            Serial.println ();
        }
    }
}

Вывод:

Complex Task List:
number of tasks: 12
[0][0] REPEAT:YES, DELAY:1000, ITERATIONS:-1
[0][1] REPEAT:NO, DELAY:0, ITERATIONS:0
[0][2] REPEAT:NO, DELAY:0, ITERATIONS:0
[0][3] REPEAT:NO, DELAY:0, ITERATIONS:0
[1][0] REPEAT:YES, DELAY:2000, ITERATIONS:10
[1][1] REPEAT:NO, DELAY:0, ITERATIONS:0
[1][2] REPEAT:NO, DELAY:0, ITERATIONS:0
[1][3] REPEAT:NO, DELAY:0, ITERATIONS:0
[2][0] REPEAT:NO, DELAY:3000, ITERATIONS:3
[2][1] REPEAT:NO, DELAY:0, ITERATIONS:0
[2][2] REPEAT:NO, DELAY:0, ITERATIONS:0
[2][3] REPEAT:NO, DELAY:0, ITERATIONS:0

Значит, ваша программа работает почти случайно: ошибка в том, как вы определяете массив отменяет ошибку в том, как вы его используете. Этот вид отменен метод ошибки - очень плохая практика программирования. Вы тратите память на неиспользуемые объекты complexTaskDefinition. Что еще более важно, ваш код не выражает ваших намерений, что делает программу более трудной для понять и отладить.

Исправление состоит в том, чтобы объявить одномерный массив, поскольку это всегда было намерением:

complexTaskDefinition tasks [ ] = {
    {taskC1, true, -1, 1000 },
    {taskC2, true, 10, 2000 },
    {taskC3, false, 3, 3000 },
};

Теперь вы можете проверить, что в этом массиве есть только три задачи, т.к. сообщает sizeof tasks / sizeof(complexTaskDefinition). Чтобы использовать массив, вы можете использовать указатель, если хотите:

task = &tasks[i];
// ...
Serial.print ( ( task->repeat ) ? "YES" : "NO" );

Или вы можете напрямую запросить участника tasks[i], как в

void showComplexVersion () {
    Serial.println ( F ( "\nComplex Version:" ) );
    for (uint8_t i = 0; i < TASK_COUNT; i++) {
        tasks[i].task();
    }
}

Теперь у меня к вам вопрос. Поскольку я уже объяснил ваше 1D против 2D ошибка массива в первой итерации моего ответа, почему вы упорствовали с этим в вашем отредактированном вопросе? Если вы собираетесь нагло игнорировать ответы вы получите, по крайней мере, скажите об этом в вопросе. Это может спасти время для тех, кто может подумать о том, чтобы помочь вам.

,

Я ценю тяжелую работу, которую вы проделали, чтобы объяснить мне это, и я изучаю журнал. Я действительно предполагал, что массив будет двумерным массивом, но моя проблема заключалась в определении задачи, которая будет вызываться в этом массиве. Я нашел хороший пост, который показал мне, как это сделать с обратным вызовом, и это сработало... когда родителем является файл .INO. См. этот пост: https://arduinoprosto.ru/q/68299/parent-class-callback-from-child-class, @Bob Jones