LINUX.ORG.RU

header-файлы с функциями, принимающими va_list

 , ,


1

1

Почему авторы всяких libc могут объявлять функции типа vprintf в stdio.h не инклюдя побочно stdarg.h (вдруг юзеру не нужно), а все остальные не могут? Ну, я конечно посмотрел как там сделано и там вместо va_list внутренний __синоним к нему, но это же не то что некроссплатформенно, а даже при переходе на другое libc может сломаться (у libc-шных хедеров такой проблемы понятное дело нет). И даже нормального способа выяснить инклюдил ли юзер stdarg тоже нет (можно было бы эти функции вырещать препроцессором если знать что stdarg не заинклюден - всё равно без него их вызвать не получится ведь va_list).

Что вы об этом думаете?

UPDATE Поскольку замечено систематическое непонимание сути темы, попробую ещё раз написать другими словами. Есть такая функция

int vprintf(char const *fmt, va_list arg);
Функция определена в stdio.h, тип va_list - в stdarg.h. Но stdio.h умеет показать этот прототип не инклюдя заодно stdarg.h. То есть, после инклюда stdio.h у нас уже есть vprintf, но нету va_list. Ладно, пофиг на vprintf, пусть даже его тоже не будет если нет stdarg, более важный аспект: можно инключить stdio+stdarg и пользоваться vprintf с va_list, а можно инклюдить только stdio (без stdarg) и он тоже скомпилируется, и не будет ругаться на «undefined identifier va_list» пытаясь показать прототип для vprintf. Как этого добились в libc я в курсе, но способ чисто приватный для libc и пользоваться им снаружи, не боясь сломать где-то совместимость, нельзя.

Перемещено leave из talks

★★★★★

Последнее исправление: firkax (всего исправлений: 1)

Что вы об этом думаете?

я думаю о другом :-)

va_list в конечном итоге - void *, но выровненный как положено

покуда размер void * совпадает с int, всё по меркам С ok. Во всех прочих вы получите ругать

MKuznetsov ★★★★★
()
Последнее исправление: MKuznetsov (всего исправлений: 1)
Ответ на: комментарий от MKuznetsov

покуда размер void * совпадает с int

Разве нынче они совпадают? У void* 8 байт, у инта 4 (чаще всего).

Или я че то не распарсил?:-)

@buddhist прав, декларация функции с произвольным числом аргументов это фича ЯП. Но шоб с этими аргументами разобраться унутрях ее нужен ваарг.

AntonI ★★★★★
()
Последнее исправление: AntonI (всего исправлений: 1)

Ну, я конечно посмотрел как там сделано и там вместо va_list внутренний __синоним к нему, но это же не то что некроссплатформенно

va_list – это как раз кросплатформенно, т.к. все «__синонимы» в конце-концов ведут к встроенной в компилятор фиче, потому что как входные параметры располагаются в бинарном виде зашито в компилятор для конкретной платформы (железа, ОС и пр.). В, условно, «си для arm» действуют одни соглашения, в каком-нибудь «си для STM32» действуют другие соглашения. Более того, даже для ядра и для юзерспэйс могут действовать разные соглашения по расположению входных параметров в функциях, т.к. эту часть в извращениях ни кто не ограничивает (только регламентируют, что бы уж совсем не было бардака).

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

The system stack is always located in on-chip RAM, which is faster than external RAM but is limited in size.

Относись к va_list как более-менее «стандартному» интерфейсу, который предоставляет компилятор для доступа к каждому входному параметру функции. В принципе, если хочешь, можешь свой изобрести, но кроме тебя его ни кто не поймет.

Vic
()
Последнее исправление: Vic (всего исправлений: 4)
Ответ на: комментарий от firkax

Батенька, хочете помощи - конкретней формулируйте свои запросы, телепаты все ещё в отпуске.

На те вопросы которые Вы тут озвучили ответы даны более чем исчерпывающие.

AntonI ★★★★★
()

Что вы об этом думаете?

Мы ржём над сишными проблемами. Хоспидя, заголовок подключили. Вот жеж твари! Ахахахахахахахах!

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

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)

Почему авторы всяких libc могут объявлять функции типа vprintf в stdio.h не инклюдя побочно stdarg.h (вдруг юзеру не нужно), а все остальные не могут?

Потому что они implementation, а остальные — нет

utf8nowhere ★★★
()
Ответ на: комментарий от Vic

va_list – это как раз кросплатформенно

С va_list и GCC есть проблема в выравнивании стека по 0x08 байт, что почему-то захардкожено и может порождать проблемы на различных старых ARM’овых девайсах.

Но это именно что GCC-проблемы.

EXL ★★★★★
()
Последнее исправление: EXL (всего исправлений: 1)

даже нормального способа выяснить инклюдил ли юзер stdarg тоже нет

7.16 Variable arguments <stdarg.h>

1 The header <stdarg.h> declares a type and defines four macros

7.16.1 Variable argument list access macros

1 The va_start and va_arg macros described in this subclause shall be implemented as macros, not functions.


Берёшь и проверяшь #ifdef va_start

utf8nowhere ★★★
()
Ответ на: комментарий от Vic

Vic AntonI

Да что ж такое, как вы читаете вообще? Ещё раз - тема НЕ про variadic функции. И тема НЕ про то, как использовать va_list в реализации. С этим никаких проблем нигде нет. Она про то что нельзя кроссплатформенно показать прототип с va_list в аргументах, не притягивая заодно весь stdarg, а stdio так умеет.

Дописал в первое сообщение ещё одно описание.

firkax ★★★★★
() автор топика
Ответ на: комментарий от utf8nowhere

О, спасибо, как же я не догадался. Или даже наверно лучше проверять va_arg. Потому что va_start теоретически можно реализовать функцией, забив на стандарт, а вот va_arg - точно только макросом.

Как же повезло, создавал тему с нытьём для толксов (не надеясь что у неё вообще есть решение), тему перенесли в норм раздел и даже решение неожиданно нашлось.

firkax ★★★★★
() автор топика
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от wandrien

Да, проблема. Представь, у меня есть хедер, в котором среди прочего есть функции в стиле vprintf. Я, с одной стороны, не хочу их выносить в отдельный хедер либо прятать за ifdef-ами, которые юзеру придётся вручную задавать, типа такого:

#define MYLIB_NEED_VPRINTF
#include "mylib.h"
с другой - не хочу требовать обязательного инклюда stdarg.h перед моим хедером, и с третьей - не хочу тайком инклюдить stdarg внутри, тратя на парсинг stdarg лишнее время компилятора и засоряя пространство имён там, где юзеру эти функции не нужны.

Хорошее решение - как-то определять, заинклюжен ли stdarg, и только тогда показывать этот прототип. Если stdarg не заинлюжен, то функциями с va_list-аргументами пользоваться всё равно не получится. Хорошо, мне уже подсказали как выяснить. Единственный минус остаётся что, если эта функция нужна, stdarg надо инклюдить до моего хедера, но это мелочи (а у stdio и такого требования нет, но ладно, всё-таки они на правах авторов libc могут позволить себе больше).

firkax ★★★★★
() автор топика
Ответ на: комментарий от wandrien

Проблема (была) сделать нормальный хедер, который не придётся обвешивать define-ми снаружи, и который не засоряет пространство имён посторонними вещами.

firkax ★★★★★
() автор топика

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

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

в сях можно извращаться как угодно.

alysnix ★★★
()
Ответ на: комментарий от firkax

не притягивая заодно весь stdarg

Охренеть бида какая. У меня весь ужасный stdarg.h занимает ровно 127 строчек и он абсолютно самодостаточен (не содержит инклюдов).

И Вы прямо целую тему на ЛОРе ради этого создали…

stdio так умеет

Наверное потому, что аффторам stdio под данную платформу захотелось набрать __gnuc_va_list? Под другой платформой там че нить другое будет наверное, да?

О чем разговор то, об особенностях реализации потрошков stdio?

AntonI ★★★★★
()
Ответ на: комментарий от AntonI

Аффтар возомнил себя проектировщиком системных АПИев и решил поупарываться по чистоте пространств имён вопреки здравому смыслу и принципу наименьшего удивления при чтении кода.

wandrien ★★
()
Ответ на: комментарий от AntonI

Наверное потому, что аффторам stdio под данную платформу захотелось набрать __gnuc_va_list? Под другой платформой там че нить другое будет наверное, да?

Потому что по стандарту stdio НЕ должен засорять пространство имён не своими символами. va_list - символ из stdarg, он должен появляться только после инклюда stdarg. Правило это хорошее, и желательно ему следовать во всех хедерах (не потому что стандарт это требует от libc, а потому что не надо устривать помойку).

О чем разговор то, об особенностях реализации потрошков stdio?

Прочти тему (она уже решена кстати), третий раз пересказывать не хочется.

firkax ★★★★★
() автор топика
Ответ на: комментарий от wandrien

Я нашел наконец его пронзительный крик души:

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

$ time cpp /usr/lib/gcc/x86_64-linux-gnu/11/include/stdarg.h
# 0 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stdarg.h"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stdarg.h"
# 40 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stdarg.h"
typedef __builtin_va_list __gnuc_va_list;
# 99 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stdarg.h"
typedef __gnuc_va_list va_list;

real	0m0.010s
user	0m0.010s
sys	0m0.000s

0.01 сек (время запуска процесса), аж ДЫВА typedef-а. Итить расходы ресурсов…

AntonI ★★★★★
()
Ответ на: комментарий от firkax

У тебя результат директивы #include теперь неявно и неочевидно зависит от предыдущих #include. Это - плохой дизайн.

И то, что ты приводишь в качестве причины для этого - на реальную проблему не тянет, чтобы такое городить. Именно что возомнил себя проектировщиком системных АПИев.

wandrien ★★
()
Ответ на: комментарий от wandrien

А по-моему вполне очевидно: есть stdarg - вот тебе прототип функции которая принимает va_list, нет stdarg-а - значит не надо. Вот то что от порядка инклюдов результат зависит это не совсем хорошо, но принято libc хедеры ставить до остальных (кроме ядерных sys/*.h).

firkax ★★★★★
() автор топика
Ответ на: комментарий от firkax

с другой - не хочу требовать обязательного инклюда stdarg.h перед моим хедером, и с третьей - не хочу тайком инклюдить stdarg внутри, тратя на парсинг stdarg лишнее время компилятора и засоряя пространство имён там, где юзеру эти функции не нужны.

вы какой-то особенный. если вы используете некий символ в хидере, то хидер в котором он определен НАДО включить, если нет каких- то супер возражений против этого. у вас их точно нет.

если хидер УЖЕ косвенно включен в единицу трансляции раньше вашего инклуда, не надо определять так ли это - он не будет парситься повторно из-за директивы условной компиляции, которой надо хидер оформлять.

ограничивать видимость символов надо составом хидеров, а не каким-то извратом с инклудами.

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

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от firkax

Не очевидно.

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

Не надо так делать.

AntonI ★★★★★
()

лучше включи stdarg.
Если действительно не хочешь парсить мелкий файл, можешь выдеить зависящие от stdarg функции в отдельный хидер. Иначе кто-нибудь будет жаловаться, что эти функции отсуттвуют

mittorn ★★★★★
()
Ответ на: комментарий от alysnix

вы какой-то особенный. если вы используете некий символ в хидере, то хидер в котором он определен НАДО включить, если нет каких- то супер возражений против этого. у вас их точно нет.

Прочти ещё раз тему и не сражайся со своими выдумками.

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

Всё ясно с тобой, иди мимо.

firkax ★★★★★
() автор топика
Ответ на: комментарий от AntonI

Речь про С++ как я понял? Ну там, где в хидерах пишут код, наверно другая ситуация. А тут в них только интерфейсы, на кодогенерацию (кроме легко локализуемых undefined identifier ошибок из-за забывчивости) они не влияют никак.

firkax ★★★★★
() автор топика
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от firkax

Это не важно, C++ или С, с инклюдами там одинаковые сложности.

Категорически не надо делать так, что бы результат зависел от порядка инклюдов. Никогда. Это аксиома.

AntonI ★★★★★
()
Ответ на: комментарий от firkax

читайте кодегайды по хидерам уже.

хидер как МИНИМУМ должен быть:

  1. самодостаточным - то есть компилироваться без ошибок. проще говоря иметь все нуждные инклуды. для этого хидер модуля some_module.h включается в модуль some_module.cpp первым, а не какие-то «системные», «библиотечные» и проч.

  2. иметь защиту от множественного включения - типа #pragma once

  3. семантика определений в хидере не зависит от порядка его включения. это связано с пунктом 1

  4. не иметь инклудов НЕ НУЖНЫХ для того, чтобы быть самодостаточным.


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

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)

но это же не то что некроссплатформенно

Нет. Это кроссплатформено, так как подключаются те условные части хедеров, что отвечают за эту платформу. Некроссплатформено будет только если ВЫ возьмете эту реализацию и потянете с собой в платформу, где для эти условные части не реализованы, а не воспользуетесь stdio.h для вашей платформы. Что будет лишь верхушкой айсберга в этих проблемах. Правильнее тут - компиляторно зависимое с использованием нестандартных расширений языка. Но проблема то вами озвучена правильно: для printf(fmt, ...) stdarg,h - не нужен, а нужен только для малой части stdio - v-функций. Но спич ваш - ну почему в этом мире всё так не просто, то есть обычное нытье.

vodz ★★★★★
()
Ответ на: комментарий от vodz

Нет. Это кроссплатформено,

Использовать __builtin_va_list - кроссплатформенно? (если ты внимательнее прочтёшь текcт то речь там именно про это) Продолжение рассуждений вообще не понял о чём.

а нужен только для малой части stdio - v-функций

Речь была исключительно про них. Всё остальное ни при чём. И дело как раз в том что им НЕ нужен stdarg (проверь сам если решишь возражать).

то есть обычное нытье

Так смотри теги, и тема была в толксах изначально.

firkax ★★★★★
() автор топика
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от firkax

Использовать __builtin_va_list - кроссплатформенно?

Я назвал правильное описание проблемы, в которое «некросплатформенное» включает в себя в узком понимании, потому ответ таки - так называть нельзя.

vodz ★★★★★
()