Я хотел бы найти / реализовать c ++ (надеюсь, stl) совместимый поток, который поддерживает несколько точек записи. Что я имею в виду под несколькими точками письма, легко объяснить с помощью следующего примера. Допустим, вы хотите создать файл исходного кода, скажем, на языке Java. Когда вы дойдете до точки, где вы хотите написать строку кода, которая зависит от конкретного пакета — вы можете написать:
stream << "import java.applet.*;";
stream << "public class MyApplet extends Applet {";
stream << "...";
stream << "}";
Обратите внимание, что у вас уже могут быть определены другие классы. Новый класс должен идти вниз, но импорт должен идти вверх.
Я знаю, что я мог бы решить эту проблему другими способами. Я думаю, что было бы здорово, если бы я мог решить эту проблему с помощью некоторого шаблона stl stream.
Boost tee с какой-то фильтрацией может работать?
Примечание: расположение может быть основано на анализе входных данных или указании выделенного типа, в этом случае я мог бы проверить, начинается ли строка с импорта или I для записи
stream << Import("java.applet.*);
где Import — это класс, который соответственно сериализуется.
Как вы себя чувствуете — это то, что может стоить усилий?
В порядке возрастания сложности:
Во-первых, просто используйте разные переменные для каждого из мест, которые вы можете написать. Затем свяжите данные, которые вы положили в каждый поток, когда закончите.
Как второй уровень сложности, хранить std::tuple< std::ofstream, std::ofstream, std::ofstream > straems
затем используйте std::get<0>(streams)
чтобы получить первый. Если вы хотите имена, используйте enum { first_stream, second_stream_name, third_stream_name }
и передать это std::get
,
Для самого сложного ответа … ну, это беспорядок.
Первый шаблон метапрограммирования шаблона:
template<typename T, typename Tags, typename=void>
struct index_of {};
template<typename T, template<typename...>class Pack, typename Tag0, typename... Tags>
struct index_of<T, Pack<Tag0, Tags...>, typename std::enable_if< std::is_same<T, Tag0>::value >::type >
: std::integral_constant< int, 0 > {};
template<typename T, template<typename...>class Pack, typename Tag0, typename... Tags>
struct index_of<T, Pack<Tag0, Tags...>, typename std::enable_if< !std::is_same<T, Tag0>::value >::type >
: std::integral_constant< int, index_of<T, Pack<Tags...> >::value + 1 > {};
template<typename Src, template<typename...>class Pack>
struct copy_types {};
template<template<typename...>class Lhs, typename... Ts, template<typename...>class Target>
struct copy_types< Lhs<Ts...>, Target > {
typedef Target<Ts...> type;
};
template<typename Src, template<typename...>class Pack>
using CopyTypes = typename copy_types<Src, Pack>::type;
template<typename Pack, typename T>
struct append {};
template<template<typename...>class Pack, typename... Ts, typename T>
struct append<Pack<Ts...>, T> {
typedef Pack<Ts..., T> type;
};
template<typename Pack, typename T>
struct Append = typename append<Pack, T>::type;
template<template<typename...>class Pack, typename T, std::size_t N>
struct repeat {
typedef Append< repeat< Pack, T, N-1 >::type, T > type;
};
template<template<typename...>class Pack, typename T>
struct repeat< Pack, T, 0 > {
typedef Pack<> type;
};
template<template<typename...>class Pack, typename T, std::size_t N>
using Repeat = typename repeat<Pack, T, N>::type;
Теперь, потому что это весело, помеченный кортеж:
template<typename T, typename Tags>
struct type_based_map;
template<typename T, template<typename...>class Pack, typename... Tags>
struct type_based_map< T, Pack<Tags...> > {
Repeat< std::tuple, T, sizeof...(Tags) > data;
template<typename Tag>
T& get() {
return std::get< index_of< Tag, std::tuple<Tags...> >::value >( data );
}
template<typename Tag>
T& get() const {
return std::get< index_of< Tag, std::tuple<Tags...> >::value >( data );
}
template<typename... Args, typename=typename std::enable_if< sizeof...(Args) == sizeof...(Tags) >::type >
explicit type_based_map( Args&&... args ):data( std::forward<Args>(args)... ) {}
type_based_map( type_based_map&& ) = default;
type_based_map( type_based_map const& ) = default;
type_based_map( type_based_map& ) = default;
};
Теперь до мяса и картошки. Поли-поток:
template<typename Tag, typename U>
struct TaggedData {
U&& data;
explicit TaggedData( U&& u ):data(std::forward<U>(u)) {}
TaggedData(TaggedData &&) = default;
TaggedData(TaggedData const&) = default;
TaggedData(TaggedData &) = default;
};
template<typename Tag>
struct DataTagger {
template<typename U>
TaggedData<U> operator()( U&& u ) const {
return {std::forward<U>(u)};
}
};
template<typename base_stream, typename Tags>
struct tagged_stream: type_based_map< base_stream, Tags >
{
using type_based_map< base_stream, Tags >::type_based_map< base_stream, Tags >;
};
template<typename base_stream, typename Tags, typename Tag, typename U>
auto operator<<( tagged_stream<base_stream, Tags>& stream, TaggedData<Tag, U> data )
->declval( stream.get<Tag>() << std::forward<U>(data.u) )
{ return ( stream.get<Tag>() << std::forward<U>(data.u) ); }
который после устранения ошибок дает следующий синтаксис:
struct bob {};
struct harry {};
struct alice {};
static DataTagger<bob> Bob;
static DataTagger<harry> Harry;
static DataTagger<alice> Alice;
typedef tagged_stream< std::ofstream, std::tuple<bob, harry, alice> > multi_out;
multi_out os;
os.get<bob>().open("bob.txt");
os.get<harry>().open("harry.txt");
os.get<alice>().open("alice.txt");
os << Bob(7) << " is seven in Bob\n";
os << Harry("hello") << " in Harry\n";
os << Alice(3.14) << " is baked by Alice\n";
что может или не может быть то, что вы ищете.
Это далеко не отлажено и, вероятно, еще не скомпилировано.
Честно? Просто есть разные переменные для каждого подпотока. Вы все равно захотите объединить их вместе вручную в конце.
Других решений пока нет …