2015-01-22 3 views
5

Я думаю о следующей проблеме: Я хочу запрограммировать микроконтроллер (скажем, тип AVR мега) с программой, которая использует какие-то таблицы поиска.Как заставить GCC оценивать функции во время компиляции?

Первой попыткой было бы найти таблицу в отдельном файле и создать ее с использованием любого другого языка/программы для сценариев/.... В этом случае необходимо приложить необходимые исходные файлы для C.

Теперь я думал использовать препроцессор и компилятор для обработки вещей. Я пытался осуществить это с помощью таблицы синусов значений (только в качестве примера):

#include <avr/io.h> 
#include <math.h> 

#define S1(i,n) ((uint8_t) sin(M_PI*(i)/n*255)) 
#define S4(i,n) S1(i,n), S1(i+1,n), S1(i+2,n), S1(i+3,n) 

uint8_t lut[] = {S4(0,4)}; 

void main() 
{ 
    uint8_t val, i; 

    for(i=0; i<4; i++) 
    { 
     val = lut[i]; 
    } 
} 

Если я скомпилировать этот код я получаю предупреждение о функции sin. Далее в сборке ничего нет в разделе .data. Если я просто удалю sin в третьей строке, я получу данные в сборке. Очевидно, что вся информация доступна во время компиляции.

Можете ли вы сказать мне, есть ли способ достичь того, что я намерен: компилятор вычисляет столько значений, сколько возможно в офлайн-режиме? Или это лучший способ использовать внешний скрипт/программу/... для вычисления записей в таблице и добавления их в отдельный файл, который будет только #include d?

+1

«довольно много усилий». - с хорошим скриптингом язык? конечно, меньше, чем атаковать проблему с C .... –

+0

C++ 11 (улучшенный с C++ 14) имеет 'constexpr' как подсказку компилятору для выполнения функции во время компиляции. – johannes

+0

@johannes: 'constexpr' не является преимуществом. Это просто _allows_ выражение, которое оценивается во время компиляции (и даже принуждение к нему компилятора, например, путем присвоения перечислению, не мешает повторной оценке во время выполнения в другом месте в том же исходном файле!). Он не обеспечивает и не намекает на что-либо. Тем не менее, мой GCC оптимизирует фрагмент кода в OP просто отлично в таблицу поиска с оценкой (без специального специального танца). – Damon

ответ

4

Общая проблема здесь состоит в том, что sin вызова делает эту инициализацию де-факто незаконным, в соответствии с правилами языка C, так как это не постоянного выражения самого по себе, и вы инициализации массива статической продолжительности хранения, что требует что. Это также объясняет, почему ваш массив не находится в разделе .data.

С11 (N1570) §6.6/2,3 Константных выражения (курсив мой)

Постоянное выражение может быть оценено в процессе перевода, а не во время выполнения , и, соответственно, может быть использовано в любом месте, константа может быть be.

Постоянные выражения должны не содержать назначение, приращение, декремент, вызова функции, или запятая операторы, кроме случаев, когда они содержатся в подвыражения, который не evaluated.115)

Однако, по комментарию @ ShafikYaghmour, GCC заменит sin вызов функции со своим встроенным партнером (если не указан параметр -fno-builtin), который, скорее всего, будет рассматриваться как постоянное выражение.По 6.57 Other Built-in Functions Provided by GCC:

GCC включает в себя встроенные версии многих функций в стандартной библиотеки C. Версии с префиксом __builtin_ всегда рассматриваются как имеющие то же значение, что и функция библиотеки C, даже если вы указываете опцию -fno-builtin.

+1

Это, вероятно, работает, потому что в настоящее время [gcc рассматривает math builtins как это были постоянные выражения] (http://stackoverflow.com/q/27744079/1708801), используя '-fno-builtin' делает сбой кода с использованием' gcc', но он генерирует предупреждение иначе. –

+0

@ShafikYaghmour: Почему тогда нет данных при разборке. Я совершенно не уверен, что произойдет, если я попытаюсь получить доступ к данным ... –

+0

@ChristianWolf: Вы можете заставить его использовать '__builtin_sin', но я согласен с Шафиком, что он, вероятно, сработает. Может быть, вы используете какой-то порт avr-gcc, который его не поддерживает? –

2

То, что вы пытаетесь, не является частью языка C. В подобных ситуациях, я написал код следуя схеме:

#if GENERATE_SOURCECODE 
int main (void) 
{ 
    ... Code that uses printf to write C code to stdout 
} 
#else 
    // Source code generated by the code above 
    ... Here I paste in what the code above generated 

    // The rest of the program 
#endif 

Каждый раз, когда вам нужно изменить его, вы запустите код с GENERATE_SOURCECODE определен, и вставить в выходной. Хорошо работает, если ваш код является автономным, и сгенерированный вывод только когда-либо изменяется, если его генерирует код.

+0

У вас есть начало правильного решения. – Joshua

+0

Это не оптимально в том смысле, что я перекрестно компилирую. Таким образом, это может быть или может быть не совсем нормально. Я думаю, что усилие выходит за рамки более высоких, чем двух разных файлов, так как все файлы заголовков должны быть обменены и, следовательно, внутри блоков '# if'. Выглядит противно, извините. –

2

Прежде всего, само собой разумеется, что вы должны оценить (возможно, опыт), стоит ли этого делать. Ваша таблица поиска будет увеличивать ваши размеры данных и усилия программистов, но может или не может обеспечить увеличение скорости выполнения, которое вам нужно.

Если вы все еще хотите это сделать, я не думаю, что препроцессор C может сделать это прямо, потому что у него нет возможностей для итерации или рекурсии.

Самый надежный способ сделать это - написать программу на языке C или на каком-либо другом языке, чтобы распечатать источник C для таблицы, а затем включить этот файл в свою программу с помощью препроцессора. Если вы используете такой инструмент, как make, вы можете создать правило для создания файла таблицы и связать файл .c с этим файлом.

С другой стороны, если вы уверены, что вы никогда не собираетесь изменить эту таблицу, можно написать программу, чтобы создать его один раз и просто вставить его в