Как я могу вызвать функцию-член через оператор вставки потока?

Используя gcc 4.8 с включенным C ++ 11, у меня есть такой класс:

class OutStream {
public:
OutStream& operator<<(const char* s);
OutStream& operator<<(int n);
OutStream& operator<<(unsigned int n);
// ...
OutStream& vformat(const char* fmt, __VALIST args);
OutStream& format(const char* fmt, ...);
};

Когда я использую этот класс, вызывая операторы напрямую, он работает как я ожидал:

OutStream out;
out.operator<<(1).format(" formatted %04X ", 2).operator<<("3\n");

Выход:

1 formatted 0002 3

Теперь я хотел бы получить тот же результат, но с помощью << потоковая запись, может быть, так:

OutStream out;
out << 1 << format(" formatted %04X ", 2) << "3\n";

Конечно, это не скомпилируется, потому что не было такого оператора для потоковой передачи OutStream.format() метод.

Там может быть решение, где format() была свободная функция, которая возвращает строку, но для этого нужно сначала записать все выходные данные format() в буфер. Мне нужно решение без std::string или другое использование кучи или буфера — в лучшем случае решение, которое создает почти такой же код, как и при непосредственном вызове операторов.

Какие-либо предложения?

Изменить, 2014-10-20:

  • Для лучшего понимания моих требований: я нахожусь на голой железной разработке, используя GCC-рычажного встроен GCC Cross Toolchain.
  • Мне нужно применить решение для некоторых различных встроенных целевых систем (большинство из них Cortex-M0 / M3 / M4). Некоторые из них имеют очень ограниченные ресурсы (Рам & Flash) и часть моих целевых систем должны работать без использования кучи.
  • По некоторым причинам я не использую Stl iostream, Тем не менее iostream тег был установлен seh edit; Я бы оставил его установленным из-за тематического соответствия, и найденное решение для моей проблемы также может быть применимо для Stl iostream,

3

Решение

Использование C ++ 14 index_sequence (Есть миллион различных реализаций на SO):

template <typename...Ts>
class formatter {
const char* fmt_;
std::tuple<Ts...> args_;

template <std::size_t...Is>
void expand(OutStream& os, std::index_sequence<Is...>) && {
os.format(fmt_, std::get<Is>(std::move(args_))...);
}

public:
template <typename...Args>
formatter(const char* fmt, Args&&...args) :
fmt_{fmt}, args_{std::forward<Args>(args)...} {}

friend OutStream& operator << (OutStream& os, formatter&& f) {
std::move(f).expand(os, std::index_sequence_for<Ts...>{});
return os;
}
};

template <typename...Args>
formatter<Args&&...> format(const char* fmt, Args&&...args) {
return {fmt, std::forward<Args>(args)...};
}

DEMO

Компилятор должен легко иметь возможность встроить операцию formatter и исключить временный объект. Действительно эта функция:

void test_foo() {
OutStream out;
out << 1 << format(" formatted %04X ", 2) << "3\n";
}

результаты в сборке (g ++ 4.9.0 -std = c ++ 1y -O3 для x64):

.LC0:
.string " formatted %04X ".LC1:
.string "3\n"test_foo():
pushq   %rbx
movl    $1, %esi
subq    $16, %rsp
leaq    15(%rsp), %rdi
call    OutStream::operator<<(int)
movl    $2, %edx
movl    $.LC0, %esi
movq    %rax, %rbx
movq    %rax, %rdi
xorl    %eax, %eax
call    OutStream::format(char const*, ...)
movq    %rbx, %rdi
movl    $.LC1, %esi
call    OutStream::operator<<(char const*)
addq    $16, %rsp
popq    %rbx
ret

так что все правильно встроено; нет никаких следов formatter в произведенном коде.

5

Другие решения

Есть три точки расширения для класса std::basic_ostream И его operator<< что выглядит актуально здесь:

  • «Вставить» функцию, которая принимает и возвращает std::ios_base&,
  • «Вставить» функцию, которая принимает и возвращает std::basic_ios<C, T>&,
  • «Вставить» функцию, которая принимает и возвращает std::basic_ostream&,

К сожалению, все три работают с указателями на функции, а не std::function случаи, что затрудняет поставку закрытия. В вашем случае вы хотели бы предоставить строку формата — и, возможно, аргументы формата — а-ля std::setw().

Вы можете найти обсуждение того, как реализовать эти манипуляторы, в старом эссе Кей Хорстманн. Расширение библиотеки iostream. В частности, посмотрите в разделе 3 «Манипуляторы», чтобы увидеть, как вы можете вернуть объект из вашего format() функция, которая служит закрытием, и написать operator<<() функция для этого объекта.

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

3

Попробуйте следующее:

OutStream out;
(out << 1).format(" formatted %04X ", 2) << "3\n";
2

Рассмотреть возможность использования GNU’s autosprintf. Это очень мало. Нет, действительно. По сути, это обертка вокруг vasprintf, Все, что нужно autosprintf — это std::string реализация и ваши обычные автономные заголовки C. Здесь заголовочный файл а также документация. Пример того, как вы можете использовать это:

OutStream out;
out << 1 << gnu::autosprintf(" formatted %04X ", 2) << "3\n";

(На самом деле, если вы используете строки фиксированного размера, вы можете изменить это, чтобы избежать любого использования std::string совсем. Конечно, есть еще предположение, что вы реализовали vasprintf и некоторая форма выделения кучи.)

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector