Передача класса с функцией преобразования для const char * через переменную функцию наподобие printf - PullRequest
0 голосов
/ 23 октября 2019

У меня есть класс SpecialString. У него есть функция перегрузки / преобразования оператора, которую он использует каждый раз, когда его выдают как const char *. Затем он возвращает нормальную c-строку.

class SpecialString
{
...
operator char* () const { return mCStr; }
...
};

Раньше это работало очень давно (буквально 19 лет назад), когда я передавал их непосредственно в printf (). Компилятор был достаточно умен, чтобы знать, что аргумент должен был быть char *, и он использовал функцию преобразования, но теперь g ++ жалуется.

SpecialString str1("Hello"), str2("World");
printf("%s %s\n", str1, str2);

ошибка: невозможно передать объект не POD типа 'SPECIALSTRING' (он же 'SpecialString') через метод с переменным числом аргументов;вызов будет прерван во время выполнения [-Wnon-pod-varargs]

Есть ли способ заставить это работать снова без изменения кода? Я могу добавить функцию перегрузки оператора deref, которая возвращает строку c и передает объекты SpecialString следующим образом.

class SpecialString
{
...
operator CHAR* () const { return mCStr; }
char* operator * () const { return mCStr; }
...
};
SpecialString str1("Hello"), str2("World");
printf("%s %s\n", *str1, *str2);

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

Ответы [ 3 ]

0 голосов
/ 23 октября 2019

Поскольку вы не хотите изменять существующий код, вместо этого вы можете написать «хак». В частности, куча перегрузок на printf(), которые исправляют существующий код.

Например:

int printf(const char* f, const SpecialString& a, const SpecialString& b)
{
    return printf(f, (const char*)a, (const char*)b);
}

С этой функцией, объявленной в вашем заголовке, каждый вызов printf() сэти конкретные параметры будут использовать эту функцию вместо «реального» * ​​1008 *, с которым вы знакомы, и выполнять необходимые преобразования.

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

0 голосов
/ 23 октября 2019

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

С классом Microsoft CString кажется, что неопределенное поведение использовалось именно так (как это происходит сработа), что теперь макет определяется таким образом, что он все еще будет работать. См. Как передать CString в строку формата% s? .

В нашей базе кода я пытаюсь исправить код, когда я изменяю файл, чтобы явно выполнить преобразование, вызывая GetString()

Есть несколько вещей, которые вы могли бы исправитьсделайте:

  • Исправьте существующий код везде, где вы получите предупреждение .

    В этом случае именованная функция, такая как c_str или GetString, предпочтительнее оператора преобразования, чтобы избежать явного приведения (например, static_cast или даже в худшем случае в стиле C * 1025). *. Оператор deref может быть приемлемым компромиссом.

  • Использовать некоторую библиотеку форматирования

    • <iosteam>
    • fmt: https://github.com/fmtlib/fmt
    • много других вариантов (поиск библиотека форматирования C ++ или что-то подобное)
  • Используйте функцию шаблонов переменных, чтобыпреобразование может быть выполнено.

  • Если вы используете только несколько типов (int, double, string) и редко более 2 или 3 параметров, определение перегрузок также может быть возможным.
  • Не рекомендуется: Взломать ваш класс, чтобы он снова работал.

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

Примечание: Я думаючто следует избегать определения своего собственного строкового класса. В большинстве случаев следует использовать стандартную строку C ++ (или представление строки). Если вам нужны дополнительные функции, я бы порекомендовал вам написать автономную функцию в пространстве имен, например, StringUtilities. Таким образом, вы избегаете преобразования между вашей собственной строкой и стандартной строкой (или некоторой строкой библиотеки, такой как MFC, Qt или чем-то еще).

0 голосов
/ 23 октября 2019

Вы можете отключить предупреждение, если вы не хотите, чтобы о нем сообщали ... но это плохая идея.

Поведение программы не определено, вы должны исправить это, и это требуетизменение кода. Вы можете использовать существующий оператор преобразования с static_cast, или вы можете использовать свою идею унарного * оператора, которая будет более краткой.

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

...