Форум программистов, компьютерный форум, киберфорум
C (Си)
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.59/34: Рейтинг темы: голосов - 34, средняя оценка - 4.59
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472

Как подставить значение макроса в строку?

13.08.2017, 08:09. Показов 7512. Ответов 7
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
При использовании макросов-констант препроцессора иногда возникает надобность подставлять их значения в строковые литералы программы для вывода всяких служебных сообщений, в том числе оповещающих об ошибках программы.

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


C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* ~=выражение=~ - это псевдокод, означающий, что в строку должна быть
   подставлена запись, соответствующая данному выражению.
   Т. е. ~=выражение=~ - это символьное строковое представление для данного
   выражения.                                                                 */
 
 
#include <stdio.h>
#include <string.h>
 
#define L 8
 
 
int main()
{
 char a[L];
 
 fgets(a, L, stdin);
 
 if(a[strlen(a) - 1] != '\n')
   fprintf(stderr,
           "The input string is too long. "
           "The size of the buffer array is ~=L=~ bytes, "
           "so the length of the input string should not exceed "
           "the limit of ~=L-2=~ symbols.\n");
 
 fputs(a, stdout);
 
 return 0; }

Суть примера состоит в следующем. Сначала я считываю строку из стандартного потока ввода в маленький буферный массив символов a[] (для этого я использую функцию fgets()). Длину массива a[] я задаю через ранее определённый макрос L, что позволяет мне легко менять этот параметр. Если размер строки оказался достаточно мал и она целиком вошла в буфер, я после этого просто вывожу ту же самую строку в стандартный поток вывода. Если же длина строки превысила длину буфера и она вошла в него неполностью, перед тем как вывести вошедшую в буфер её часть, я оставляю в потоке stderr сообщение об ошибке.

В этом сообщении мне нужно указать, что
1) размер буфера считывания составляет L байт;
2) входная строка превысила максимально допустимый предел в L-2 символа.

В своём примере на псевдокоде я обозначил оба числа — L и L-2 — как ~=L=~ и ~=L-2=~. В строку вместо записей L и L-2 должно быть подставлено строковое символьное представление их значений.

Можно было бы, конечно, вместо L и L-2 в строке сразу же написать 8 и 6 соответственно, ибо L == 8, а L-2 == 6, но это неудобно, поскольку чуть только я изменю значение макроса L в макроопределении #define, я вынужден буду вручную менять эти два числа в строке (сами автоматически они в этом случае не изменятся).

Для того чтобы подставить макрос L в строку, я могу использовать следующий приём. Введу в текст программы следующие два макроопределения

C
1
2
#define stringify(s)    substringify(s)
#define substringify(s) #s
С помощью макроса stringify() я теперь легко смогу внедрить значение первого параметра — L — в строку:

C
1
2
3
4
5
   fprintf(stderr,
           "The input string is too long. "
           "The size of the buffer array is " stringify(L) " bytes, "
           "so the length of the input string should not exceed "
           "the limit of 6 symbols.\n");
Но второй параметр — L-2 — мне с помощью этого трюка подставить не удастся, т. к. препроцессор раскрывает свои макросы, но не вычисляет их. Если я напишу в своей строке

C
1
2
3
4
"The input string is too long. "
"The size of the buffer array is " stringify(L) " bytes, "
"so the length of the input string should not exceed "
"the limit of " stringify(L-2) " symbols.\n"
она у меня раскроется в следующее:

C
1
2
"The input string is too long. The size of the buffer array is 8 bytes, "
so the length of the input string should not exceed the limit of 8-2 symbols.\n"
т. е. вместо 6 будет стоять 8-2.


Вот пример такой не совсем правильной программы.


C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <string.h>
 
#define stringify(s)    substringify(s)
#define substringify(s) #s
 
#define L 8
 
 
int main()
{
 char a[L];
 
 fgets(a, L, stdin);
 
 if(a[strlen(a) - 1] != '\n')
   fprintf(stderr,
           "The input string is too long. "
           "The size of the buffer array is " stringify(L) " bytes, "
           "so the length of the input string should not exceed "
           "the limit of " stringify(L-2) " symbols.\n");
 
 fputs(a, stdout);
 
 return 0; }

Конечно, я могу решить вопрос, используя возможности форматируемого вывода функции fprintf(), а именно, записав так:

C
1
2
3
4
5
   fprintf(stderr,
           "The input string is too long. "
           "The size of the buffer array is %d bytes, "
           "so the length of the input string should not exceed "
           "the limit of %d symbols.\n", L, L-2);
Но тогда выходная строка будет вычисляться и все подстановки в неё будут производиться на этапе исполнения программы, а не на этапе работы препроцессора и компилятора, несмотря на то что окончательный вид этой строки заведомо известен и мог бы быть вычислен ещё во время препроцессирования или компиляции. Очень хотелось бы, чтобы в моём примере компилятором формировалась готовая законченная строка и именно она в готовом виде использовалась работающей программой, а не происходило формирования этой строки сообщения уже во время работы программы, что не совсем разумно.

Что интересно, с числом L эту трудность решить удалось (через пару макросов stringify() и substringify()), а вот с числом L-2 такое решение не проходит. Есть ли у кого какие мысли, как решить эту задачу в более общем случае, чтобы решение работало и для L-2? Может, препроцессор позволяет вычислять некоторые выражения путём задания каких-то директив или команд, или есть трюк, позволяющий выкрутиться как-то по-другому и решить эту задачу? Жду с интересом ваших идей и предложений.

Все свои исходники выкладываю на всякий случай в виде архива (см. прилагаемый файл).
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
13.08.2017, 08:09
Ответы с готовыми решениями:

Подставить значение переменной в строку
Здравствуйте. Усть ф-ция для перебора и вывода полей базы в селект. Приведу ту часть где даные выводятся в опшион. $html .= '&lt;option ...

Как подставить значение переменной var1 чтобы читалось его значение
Подскажите, не могу понять как подставить значение переменной var1 чтобы читалось его значение : dim T1$ Dim var1 As Integer Dim var2...

Параметр в строку подставить как?
Добрый день пишу @set dd = %DATE:~0,2% @set mm = %DATE:~3,2% @set yyyy = %DATE:~6,4% @set needFormat = dd_mm @set login =...

7
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
13.08.2017, 08:26  [ТС]
String_Macrosubstitution.rar
0
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
14.08.2017, 03:38  [ТС]
Ну, парни, какие идеи есть?
Вопрос мой ясен, я надеюсь?
0
 Аватар для HighPredator
6045 / 2160 / 753
Регистрация: 10.12.2010
Сообщений: 6,005
Записей в блоге: 3
14.08.2017, 13:06
Простого и хорошо читаемого способа нет. Все упирается в то, что это как ни крути текстовая замена. Да, дойдя до стадии L-2 -> 8-2 вы максимально приблизитесь к результату, но это потолок читабельности. Если нужно, то да, можно извернуться, обернув это выражение в функция-подобный макрос, и даже извернуться, заставив препроцессор сделать повторный скан и развертку, что уже само по себе будет монструозно, но реализовывать арифметику гробовым набором макросов вам все равно придется именно для получения значения.
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
14.08.2017, 14:28
Цитата Сообщение от JohnyWalker Посмотреть сообщение
Но тогда выходная строка будет вычисляться и все подстановки в неё будут производиться на этапе исполнения программы, а не на этапе работы препроцессора и компилятора
Ну и что? Так важно сэкономить лишние несколько тактов в вызове fprintf'а, который занимает десятки тысяч тактов?

Присоединюсь к коллеге - нормальных способов нет. Можно немного извращённым способом. Это будет наименьшее из зол

C
/* При изменении значения надо модифицировать оба макроса */
#define L 8
#define L_MINUS_2 6
...
/* На всякий случай подстрахуемся от того, что кто-то изменил только
 * один из макросов */
ASSERT (L - 2 == L_MINUS_2);
...
fprintf (.... L ... L_MINUS_2 ...)
Добавлено через 2 минуты
Цитата Сообщение от JohnyWalker Посмотреть сообщение
Вопрос мой ясен, я надеюсь?
Вопрос ясен. Не понятно, зачем

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Может, препроцессор позволяет вычислять некоторые выражения путём задания каких-то директив или команд
Не позволяет

Цитата Сообщение от JohnyWalker Посмотреть сообщение
или есть трюк, позволяющий выкрутиться как-то по-другому и решить эту задачу?
Трюк есть, только руки отвалятся столько мусора писать ради непонятно чего. Новая версия форумного движка косячит при печати кода, но разобраться можно
"Циклы" на препроцессоре
1
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
15.08.2017, 02:03  [ТС]
Цитата Сообщение от Evg Посмотреть сообщение
Ну и что? Так важно сэкономить лишние несколько тактов в вызове fprintf'а, который занимает десятки тысяч тактов?

Вопрос ясен. Не понятно, зачем
Да нет. Ну цель всего этого - достичь алгоритмической чистоты получившейся программы, пусть даже немножко в ущерб читаемости её исходного текста. Ясно, что и L, и L-2 в моём примере - это литералы, вернее символические обозначения для таких литералов, и подразумевается вхождение этих литералов в строку. Эти литералы могли бы быть подставлены в строку ещё во время компиляции, и нет особого смысла вычислять эту строку во время исполнения программы. Я понимаю, что с практической точки зрения вопрос не стоит выеденного яйца, экономия ничтожна, но всё же... А потом хочется на этом конкретном примере понять возможности и ограничения самого языка и его препроцессора. Хочется понять, что можно при желании выжать из языка, а что выжать из него уже не получится.

Засунуть макрос L в строку на этапе компиляции у меня получилось, а уже попытка всунуть в неё вычисленное выражение L-2 вызвала сложности.

А потом, тот пример, который я здесь привёл в качестве демонстрации, он совсем простенький и решение через fprintf() во время исполнения программы не вызывает никаких вопросов. В той же небольшой (но не демонстрационной) программке, которую я писал для себя, такое решение вызовет усложнение кода. Функция (очень примитивная), которая могла бы занимать 2 строчки кода, будет занимать, допустим, 10 - 15 строк из-за такой вот ерунды (необходимости подстановок в строку fprintf'ом). Т. е. простейшее решение придётся усложнить и изуродовать. Думаю, ты понимаешь.


Цитата Сообщение от Evg Посмотреть сообщение
Присоединюсь к коллеге - нормальных способов нет. Можно немного извращённым способом. Это будет наименьшее из зол

C
1
2
3
4
5
6
7
8
9
/* При изменении значения надо модифицировать оба макроса */
#define L 8
#define L_MINUS_2 6
...
/* На всякий случай подстрахуемся от того, что кто-то изменил только
 * один из макросов */
ASSERT (L - 2 == L_MINUS_2);
...
fprintf (.... L ... L_MINUS_2 ...)

Да нет, ну это не очень интересно. Написать

C
1
2
3
4
5
6
7
#define L 8
#define M 6
 
fputs("Буфер имеет размер " stringify(L) " символов, "
      "поэтому размер строки не должен превосходить " stringify(M) " символов. "
      "Входная строка превысила " stringify(M) " символов, "
      "и поэтому не может быть считана.", stderr);
я бы смог. Хотелось бы, чтобы L-2 высчитывалась автоматически из L. Создавать для этого новый макрос M и вручную записывать в него посчитанное значение не хочется, тогда весь смысл этих макросов пропадает.


Цитата Сообщение от Evg Посмотреть сообщение
Трюк есть, только руки отвалятся столько мусора писать ради непонятно чего. Новая версия форумного движка косячит при печати кода, но разобраться можно
"Циклы" на препроцессоре
Я посмотрел твою запись в блоге. Суть решения мне понятна. Я представляю, как всё это можно сделать по аналогии в моём случае. Спасибо за ссылку, с интересом посмотрел. Но, может, есть ещё какое-то решение?

Добавлено через 12 минут
Цитата Сообщение от HighPredator Посмотреть сообщение
Простого и хорошо читаемого способа нет. Все упирается в то, что это как ни крути текстовая замена. Да, дойдя до стадии L-2 -> 8-2 вы максимально приблизитесь к результату, но это потолок читабельности. Если нужно, то да, можно извернуться, обернув это выражение в функция-подобный макрос, и даже извернуться, заставив препроцессор сделать повторный скан и развертку, что уже само по себе будет монструозно, но реализовывать арифметику гробовым набором макросов вам все равно придется именно для получения значения.
HighPredator, а опиши хотя бы примерно, что это за способ. Что ты имел в виду, говоря о том, что можно обернуть выражение в функциеподобный макрос, заставить препроцессор выполнить скан и развёртку и всё-таки реализовать на препроцессоре арифметику? Как это можно сделать хотя бы на уровне идей? Пусть это будет и очень громоздко, но всё равно интересно, как. Может, в сети (в статьях, блогах, на форумах, пусть даже на английском) такие решения уже обсуждались и они есть? Тогда можно было бы дать ссылку на хорошую статью или дискуссию, где всё это разобрано и решено.
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
15.08.2017, 10:57
Цитата Сообщение от JohnyWalker Посмотреть сообщение
Но, может, есть ещё какое-то решение?
Нету, т.к. функциональность препроцессора подобных вещей не содержит

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Что ты имел в виду, говоря о том, что можно обернуть выражение в функциеподобный макрос, заставить препроцессор выполнить скан и развёртку и всё-таки реализовать на препроцессоре арифметику?
Думается, примерно такую же лапшу, как и в "циклах" на препроцессоре. Только лапша получается двухмерная
1
 Аватар для HighPredator
6045 / 2160 / 753
Регистрация: 10.12.2010
Сообщений: 6,005
Записей в блоге: 3
16.08.2017, 09:27
JohnyWalker, вот нашел в закромах небольшой сборник трюков https://github.com/pfultz2/Clo... and-idioms
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
16.08.2017, 09:27
Помогаю со студенческими работами здесь

ShellExecute: как подставить переменную в строку?
подскажите как подставить переменную в строку полная строка для примера ...

Как мне подставить строку из массива в enum?
Есть массив string в котором содержатся строки dog cat bird и есть enum Animals с такими же вариантами, как мне подставить строку из...

Как из файла вывести каждую строку и подставить ее в переменную?
Есть на сервере file.txt Там имеется три строки. $fp = fopen(&quot;file.txt&quot;, &quot;r&quot;); Как сделать так, чтобы каждая строка попала в...

Как подставить выбранный IP из combobox в строку подключения к БД sqlConnection?
Добрый день! Прошу провести ликбез.. Есть программа, подключается к БД на основе жестко прописанной строки: public Mut3() { ...

Как подставить значение переменной
Как правильно написать, чтоб вместо send_form подставлялось значение переменной определенной в первой строке? var send_form =...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
Новые блоги и статьи
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
Фото: Daniel Greenwood
kumehtar 13.11.2025
Расскажи мне о Мире, бродяга
kumehtar 12.11.2025
— Расскажи мне о Мире, бродяга, Ты же видел моря и метели. Как сменялись короны и стяги, Как эпохи стрелою летели. - Этот мир — это крылья и горы, Снег и пламя, любовь и тревоги, И бескрайние. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru