constexpr и инициализация статического константного void-указателя с переинтерпретацией приведений, какой компилятор прав?

Как насчет этого?

std::string getstring()
{
    std::string input;

    // let the terminal do the line editing
    nocbreak();
    echo();

    // this reads from buffer after <ENTER>, not "raw" 
    // so any backspacing etc. has already been taken care of
    int ch = getch();

    while ( ch != '\n' )
    {
        input.push_back( ch );
        ch = getch();
    }

    // restore your cbreak / echo settings here

    return input;
}

Я бы отказался от использования альтернативного семейства функций *scanw(). Вы будете манипулировать временным буфером char [], базой *scanf() со всеми его проблемами, плюс спецификация *scanw() утверждает, что он возвращает ERR или OK вместо количества проверенных элементов, что еще больше уменьшает полезность.

Хотя getstr() (предложенный пользователем indiv) выглядит лучше, чем *scanw() и выполняет специальную обработку функциональных клавиш, для него все равно потребуется временное char [], и я стараюсь избегать C ++, если ничего, кроме как избежать произвольного размера буфера.

17
задан 101010 25 June 2014 в 01:11
поделиться

1 ответ

Я также сталкивался с этой проблемой при программировании для микроконтроллеров AVR. Avr-libc имеет заголовочные файлы (включенный до <avr/io.h>, которые делают доступными расположение регистра для каждого микроконтроллера путем определения макросы такой как [1 113]:

#define TCNT1 (*(volatile uint16_t *)(0x84))

Это позволяет использовать TCNT1, как будто это была нормальная переменная и любые чтения, и записи направлены к адресу памяти 0x84 автоматически. Однако это также включает (неявный) reinterpret_cast, который предотвращает использование адреса этой "переменной" в константном выражении. И так как этот макрос определяется avr-libc, изменение его для удаления броска не является действительно опцией (и переопределение таких макросов самостоятельно работает, но тогда требует определения их для всех различных микросхем AVR, копируя информацию от avr-libc).

, Так как складной взлом, предложенный Shafik здесь, кажется, больше не работает в gcc 7 и выше, я искал другое решение.

Рассмотрение avr-libc заголовочных файлов более тесно, оказывается, что у них есть два режима : - обычно, они определяют подобные переменной макросы как показано выше. - При использовании в ассемблере (или при включении с _SFR_ASM_COMPAT определенный), они определяют макросы, которые просто содержат адрес, например: #define TCNT1 (0x84)

На первый взгляд последний кажется полезным, так как Вы могли тогда установить _SFR_ASM_COMPAT, прежде чем будут включать <avr/io.h> и просто будут использовать intptr_t константы и использовать адрес непосредственно, а не через указатель. Однако, так как можно включать avr-libc заголовок только однажды (iow, только имейте TCNT1 или как переменная как макрос или как адрес), этот прием только работает в исходном файле, который не включает никакие другие файлы, которым была бы нужна переменная как макросы. На практике это кажется маловероятным (хотя, возможно, у Вас мог быть constexpr (класс?) переменные, которые объявлены в.h файле и присвоены значение в .cpp файле, который не включает ничто иное?).

В любом случае, я нашел другой прием Krister Walfridsson , который определяет эти регистры как внешние переменные в заголовочном файле C++ и затем определяет их и определяет местоположение их в фиксированном местоположении при помощи ассемблера.S файл. Тогда можно просто взять адрес этих глобальных символов, который допустим в constexpr выражения. Для создания этой работы этот глобальный символ должен иметь другое имя как исходный макрос регистра, для предотвращения конфликта между обоими.

, Например, в Вашем коде C++, Вы имели бы:

extern volatile uint16_t TCNT1_SYMBOL;

struct foo {
  static constexpr volatile uint16_t* ptr = &TCNT1_SYMBOL;
};

И затем Вы включаете.S файл в свой проект, который содержит:

#include <avr/io.h>
.global TCNT1_SYMBOL
TCNT1_SYMBOL = TCNT1

При записи этого, я понял, что вышеупомянутое не ограничено случаем AVR-libc, но может также быть применено к более универсальному вопросу, который задают здесь. В этом случае Вы могли получить файл C++, который похож:

extern char MY_PTR_SYMBOL;
struct foo {
  static constexpr const void* ptr = &MY_PTR_SYMBOL;
};

auto main() -> int {
  return 0;
}

И.S файл, который похож:

.global MY_PTR_SYMBOL
MY_PTR_SYMBOL = 0x1

Вот то, как это смотрит: https://godbolt.org/z/vAfaS6 (я не мог выяснить, как заставить проводник компилятора связывать и cpp и.S файл вместе, хотя

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

1
ответ дан 30 November 2019 в 12:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: