GCC передает не копию лежащего в c0, а указатель на c0, и получается полный алес! Если же убрать копи конструктор из декларации класса, то замечательно передается копия и все работает. Причем GCC сам генерит копи конструктор, который и вызывает. При моем копи конструкторе даже упоминания нет.
Вторая проблема с clang, он компилирует код с -Wno-error=non-pod-varargs, но вставляет ud2 (invalid opcode) перед сгенерированным кодом, те мы сделали что ты просил, но фиг ты это запустишь. Это можно как-то обойти?
ЗЫ. Все эксперименты проводились при выключенных оптимизациях.
ЗЫЫ. здесь можно поэксперементировать: https://godbolt.org/z/-PMwXh
Re: При портировании кода на линукс столкнулся с проблемой
Здравствуйте, gwg-605, Вы писали:
G6>Здравствуйте, watchmaker, Вы писали:
W>>Обе проблемы решаются исправлением кода в соответствии со стандартом языка:
printf( "%u--%s--%u", 0x55, c0.m_Pointer, 0xAA);
G6>Можно ссылочку в стандарте на эту часть?
Важны эти пункты:
[expr.call]
When there is no parameter for a given argument, the argument is passed in such a way that the receiving
function can obtain the value of the argument by invoking va_arg
...
Passing a potentially-evaluated argument of class type (Clause 12) having a non-trivial copy constructor, a non-trivial move constructor, or a non-trivial destructor, with no corresponding parameter, is conditionally-supported with implementation-defined semantics.
То есть нет гарантий работоспособности передачи такого типа через .... То есть это заведомо непереносимо, т.к. "implementation-defined semantics" может быть UB или отказом от компиляции.
Но если если "implementation-defined semantics" устраивает для известного множества версий компиляторов (например, как в gcc), то формально писать так можно.
clang тут прав.
gcc, с разным способом передачи в зависимости от наличия конструктора, тоже прав.
А вот чтение параметра не того типа — уже UB, кроме случаев
— one type is a signed integer type, the other type is the corresponding unsigned integer type, and the value is representable in both types;
— one type is pointer to void and the other is a pointer to a character type.
printf будет читать в соответствии с спецификатором %s, а нужно только с va_arg(…, ttt<char>) (ну и при условии предыдущего пункта).
Здравствуйте, Vamp, Вы писали:
V>Неопределенное поведение. Чего ты от компилятора хочешь?
1. В чем оно неопределенное? Кладу копию объекта в качестве параметра. Насколько я понимаю это дефолтное поведение для функций с переменным числом параметров.
Какое поведение должно быть у такого кода:
int myfunc( int myformat, ... ) ;
main() {
struct S {
int m1;
int m2 ;
};
S mys{1,1}
myfunc( 1, mys ) ;
}
при наличии у S копи конструктора и при отсутвии? Как мне угадывать в myfunc, что компилятор мне передал: копию объекта или поинтер на него?
2. Почему наличие явного копи-конструктора меняет поведение компилятора?
Re[3]: При портировании кода на линукс столкнулся с проблемой
G6>Как мне угадывать в myfunc, что компилятор мне передал: копию объекта или поинтер на него?
Угадывать не надо. Надо использовать va_arg с правильным типом (S, в данном случае). Компилятор сам возьмёт объект откуда нужно.
G6>2. Почему наличие явного копи-конструктора меняет поведение компилятора?
Для типов с нетривиальными конструкторами другие правила.
Re[4]: При портировании кода на линукс столкнулся с проблемо
Здравствуйте, watchmaker, Вы писали:
W>gcc, с разным способом передачи в зависимости от наличия конструктора, тоже прав.
W>А вот чтение параметра не того типа — уже всегда UB: W>
W>[cstdarg.syn]
W>If the parameter parmN is of a reference type, or of a type that is not compatible with the type that results when passing an argument for which there is no parameter, the behavior is undefined.
W>printf будет читать в соответствии с спецификатором %s, а нужно только с va_arg(…, ttt<char>) (ну и при условии предыдущего пункта).
то есть получается gcc целенаправленно загнал себя в UB, передав ссылку и наперёд зная, что там va_arg? Как же тут говорить о правоте компилятора?
Re[5]: При портировании кода на линукс столкнулся с проблемо
SP> передав ссылку и наперёд зная, что там va_arg?
И?
Это не запрещено и может быть использовано правильно (с gcc): https://godbolt.org/z/0aolWi — никаких проблем. Если передаётся по ссылке, то и читается по ссылке, а если передаётся по значению (после удаления конструкторов), то и читается по значению.
SP>то есть получается gcc целенаправленно загнал себя в UB, передав ссылку и наперёд зная, что там va_arg? Как же тут говорить о правоте компилятора?
Нет. У gcc всё хорошо. Проблема в исходном коде автора темы.
Тут ситуация аналогична, например, коду printf("%s", 42);. В чём тут винить компилятор?
G6>GCC передает не копию лежащего в c0, а указатель на c0, и получается полный алес!
У gcc есть отличный ключ -Werror=format , с его помощью вы сможете весь свой проект
избавить от этого ужасного кода, очень рекомендую. clang по идее тоже поддерживает этот ключ компиляции.
Здравствуйте, gwg-605, Вы писали:
G6>Здравствуйте, Vamp, Вы писали:
V>>Неопределенное поведение. Чего ты от компилятора хочешь? G6>1. В чем оно неопределенное? Кладу копию объекта в качестве параметра.
Когда printf увидит %s, то будет считать, что параметр — char*. А если там что-то другое, то что произойдёт, если переданные данные будут интерпретированы как указатель и будет обращение по этому адресу?
Компилируй с параметром -Wall, тогда компилятор будет выдавать предупреждение, если передаваемые в printf параметры не соответствуют формату. А лучше, как уже советовали -Werror=format, тогда вообще не соберёшь в этом случае.
Re[2]: При портировании кода на линукс столкнулся с проблемой
Здравствуйте, Zhendos, Вы писали:
Z>clang по идее тоже поддерживает этот ключ компиляции.
Поддерживает. При сборке с параметрами по умолчанию, clang выдаёт предупреждение при несоответствии формата и аргументов printf. А данный пример вообще не компилирует
1.cpp:19:33: error: cannot pass non-POD object of type 'ttt<char>' to variadic function; expected type from format string was 'char *' [-Wnon-pod-varargs]
printf( "%u--%s--%u", 0x55, c0, 0xAA ) ;
~~ ^~
Номер строки указан с учётом #include <stdio.h> и пустой строки в начале файла.
Re: При портировании кода на линукс столкнулся с проблемой
Прошу прощения за офтопик, но некоторое подведение итогов обсуждения.
1. Спасибо всем кто учавствовал в обсуждении, согласен использовал implementation depended вещи которые могут приводить к UB, что и получил.
2. Задача которую хотел сделать оказалась невозможна те избавиться от "мусора вида .cstr()" в printf( "%s", mystring.cstr() ) ; те в mystring лежит только один поинтер и он указывает на строку. И очень жаль, что "супер умные" компиляторы могут сделать соответствие формата с параметрами, а понять, что в классе лежит строка они не могут ((
3. Развитие С++ с каждым годом удручает, раньше ты хотел выстрелить себе в ногу, ты брал и стрелял, а сейчас тебе надо изголиться чтобы попытаться выстрелить себе в ногу, но некоторые компиляторы вообще убъют тебя раньше чем ты попытаешься выстрелить себе в ногу.
Здравствуйте, gwg-605, Вы писали:
G6>Прошу прощения за офтопик, но некоторое подведение итогов обсуждения.
[...] G6>2. Задача которую хотел сделать оказалась невозможна те избавиться от "мусора вида .cstr()" в printf( "%s", mystring.cstr() ) ; те в mystring лежит только один поинтер и он указывает на строку. И очень жаль, что "супер умные" компиляторы могут сделать соответствие формата с параметрами, а понять, что в классе лежит строка они не могут ((
в языке C++ нет printf
G6>3. Развитие С++ с каждым годом удручает, раньше ты хотел выстрелить себе в ногу, ты брал и стрелял, а сейчас тебе надо изголиться чтобы попытаться выстрелить себе в ногу, но некоторые компиляторы вообще убъют тебя раньше чем ты попытаешься выстрелить себе в ногу.
C++ следует стандарту
очень жаль что с каждым годом все больше людей хотят что бы С++ делал что то так как им хочется
а не так как написано в стандарте
Re[2]: При портировании кода на линукс столкнулся с проблемой
Здравствуйте, gwg-605, Вы писали:
G6>И очень жаль, что "супер умные" компиляторы могут сделать соответствие формата с параметрами, а понять, что в классе лежит строка они не могут ((
Понять, что лежит в классе, компилятор может. Но телепатия в компиляторе стандартом не предусмотрена, а без неё компилятору непонятно, что ты её хочешь в printf засунуть, если ты явно компилятору это не скажешь, что надо char* передать.
G6>3. Развитие С++ с каждым годом удручает, раньше ты хотел выстрелить себе в ногу, ты брал и стрелял, а сейчас тебе надо изголиться чтобы попытаться выстрелить себе в ногу, но некоторые компиляторы вообще убъют тебя раньше чем ты попытаешься выстрелить себе в ногу.
Ты и сейчас можешь в ногу себе выстрелить. Арсенал для этого большой — от водяного пистолета до РСЗО. И ты выстрелил в ногу классическим способом — передал в printf для %s не char*, а данные другого типа. Этот способ стрельбы по ногам используется со времён первых версий Си.
Re[2]: При портировании кода на линукс столкнулся с проблемой
Здравствуйте, gwg-605, Вы писали:
G6>2. Задача которую хотел сделать оказалась невозможна те избавиться от "мусора вида .cstr()" в printf( "%s", mystring.cstr() ) ;
Почему мусор? Нао лишь понимать, что mystring ДОЛЖЕН делать, а что он делать НЕ ОБЯЗАН (хотя иногда и может для собственного удобства).
Это особенно важно если смешивать стилистику C и C++, потому что одна часть код предявляет набор контрактов-требований, которые не выполняются (или выполняются частично), а сам по себе ANSI C слишком прост чтобы догадаться о предполагаемых неявностях.
Re[3]: При портировании кода на линукс столкнулся с проблемой
Здравствуйте, gwg-605, Вы писали:
G6>Прошу прощения за офтопик, но некоторое подведение итогов обсуждения.
G6>2. Задача которую хотел сделать оказалась невозможна те избавиться от "мусора вида .cstr()" в printf( "%s", mystring.cstr() ) ; те в mystring лежит только один поинтер и он указывает на строку. И очень жаль, что "супер умные" компиляторы могут сделать соответствие формата с параметрами, а понять, что в классе лежит строка они не могут ((
Почему же? вполне возможна. Вопрос лишь, насколько ты хороший танцор.
G6>3. Развитие С++ с каждым годом удручает, раньше ты хотел выстрелить себе в ногу, ты брал и стрелял, а сейчас тебе надо изголиться чтобы попытаться выстрелить себе в ногу, но некоторые компиляторы вообще убъют тебя раньше чем ты попытаешься выстрелить себе в ногу.
Да нет же, С++ прекрасен, только легаси его портит, если догадаются ввести тэг version 23 {}, жизнь наладится
По твоей задаче: если ты достаточно хороший танцор, то можно сделать так: