адаптивность
Любая попытка printf
без POD приводит к неопределенному поведению:
struct Foo {
virtual ~Foo() {}
operator float() const { return 0.f; }
};
printf ("%f", Foo());
std::string foo;
printf ("%s", foo);
Приведенные выше printf-вызовы приводят к неопределенному поведению. Ваш компилятор может действительно предупреждать вас, но эти предупреждения не требуются стандартами и невозможны для строк формата, известных только во время выполнения.
IO-Streams:
std::cout << Foo();
std::string foo;
std::cout << foo;
Судите сами.
Расширяемость
struct Person {
string first_name;
string second_name;
};
std::ostream& operator<< (std::ostream &os, Person const& p) {
return os << p.first_name << ", " << p.second_name;
}
cout << p;
cout << p;
some_file << p;
C
// inline everywhere
printf ("%s, %s", p.first_name, p.second_name);
printf ("%s, %s", p.first_name, p.second_name);
fprintf (some_file, "%s, %s", p.first_name, p.second_name);
или
// re-usable (not common in my experience)
int person_fprint(FILE *f, const Person *p) {
return fprintf(f, "%s, %s", p->first_name, p->second_name);
}
int person_print(const Person *p) {
return person_fprint(stdout, p);
}
Person p;
....
person_print(&p);
Обратите внимание, как вы должны позаботиться об использовании правильных аргументов / подписей вызова в C (например, person_fprint(stderr, ...
, person_fprint(myfile, ...
), где в C ++ "FILE
-аргумент" автоматически "выводится" из выражение. Более точный эквивалент этого вывода на самом деле больше похож на этот:
FILE *fout = stdout;
...
fprintf(fout, "Hello World!\n");
person_fprint(fout, ...);
fprintf(fout, "\n");
I18N
Мы повторно используем наше определение Person:
cout << boost::format("Hello %1%") % p;
cout << boost::format("Na %1%, sei gegrüßt!") % p;
printf ("Hello %1$s, %2$s", p.first_name.c_str(), p.second_name.c_str());
printf ("Na %1$s, %2$s, sei gegrüßt!",
p.first_name.c_str(), p.second_name.c_str());
Судите сами.
Я считаю это менее актуальным на сегодня (2017 г.). Возможно, просто интуитивное чувство, но I18N - это не то, что ежедневно делает ваш средний программист на C или C ++. Плюс, это боль в ... натомии в любом случае.
Производительность
- Вы измерили фактическое значение производительности печати? Действительно ли ваши узкие места настолько ленивы, что вывод результатов вычислений является узким местом? Вы уверены, что вам вообще нужен C ++?
- Страшный штраф за производительность должен удовлетворить тех из вас, кто хочет использовать сочетание printf и cout. Это особенность, а не ошибка!
Если вы постоянно используете iostreams, вы можете
std::ios::sync_with_stdio(false);
и получите равное время выполнения с хорошим компилятором:
#include <cstdio>
#include <iostream>
#include <ctime>
#include <fstream>
void ios_test (int n) {
for (int i=0; i<n; ++i) {
std::cout << "foobarfrob" << i;
}
}
void c_test (int n) {
for (int i=0; i<n; ++i) {
printf ("foobarfrob%d", i);
}
}
int main () {
const clock_t a_start = clock();
ios_test (10024*1024);
const double a = (clock() - a_start) / double(CLOCKS_PER_SEC);
const clock_t p_start = clock();
c_test (10024*1024);
const double p = (clock() - p_start) / double(CLOCKS_PER_SEC);
std::ios::sync_with_stdio(false);
const clock_t b_start = clock();
ios_test (10024*1024);
const double b = (clock() - b_start) / double(CLOCKS_PER_SEC);
std::ofstream res ("RESULTS");
res << "C ..............: " << p << " sec\n"
<< "C++, sync with C: " << a << " sec\n"
<< "C++, non-sync ..: " << b << " sec\n";
}
Результаты (g++ -O3 synced-unsynced-printf.cc
, ./a.out > /dev/null
, cat RESULTS
):
C ..............: 1.1 sec
C++, sync with C: 1.76 sec
C++, non-sync ..: 1.01 sec
Судите ... сами.
Нет. Вы не запретите мне мой принт.
Благодаря разнообразным шаблонам вы можете печатать на C ++ 11 в безопасных для печати типах I18N. И вы сможете получить их очень, очень производительно, используя пользовательские литералы, то есть можно будет написать полностью статическое воплощение.
У меня есть подтверждение концепции . Тогда поддержка C ++ 11 была не такой зрелой, как сейчас, но вы поняли.
Временная адаптивность
// foo.h
...
struct Frob {
unsigned int x;
};
...
// alpha.cpp
... printf ("%u", frob.x); ...
// bravo.cpp
... printf ("%u", frob.x); ...
// charlie.cpp
... printf ("%u", frob.x); ...
// delta.cpp
... printf ("%u", frob.x); ...
Позже ваши данные становятся такими большими, что вы должны сделать
// foo.h
...
unsigned long long x;
...
Это интересное упражнение, поддерживающее это и делающее его без ошибок. Особенно, когда другие несвязанные проекты используют foo.h .
прочее.
Потенциал ошибок : Существует много места для ошибок в printf, особенно когда вы добавляете строки пользовательского ввода в микс (подумайте о вашей команде I18N). Вы должны позаботиться о правильном экранировании каждой такой строки формата, вы должны обязательно передать правильные аргументы и т. Д. И т. Д.
IO-Streams делает мой двоичный файл больше : Если это более важный вопрос, чем удобство сопровождения, качество кода, возможность повторного использования, тогда (после проверки проблемы!) Используйте printf.