I want to create a system that marks up text with tags inserted into a stream. Each tag frames a part of the text, creating a tree-like structure, just as in classic markup languages (HTML, XML...). I want to externalize the formatting of the text according to the tags. I've found a way of doing this that works, but as I'm not comfortable with std::streams
in C++, I would appreciate advice and a code review for this project.
#include <iostream>
#include <sstream>
#include <vector>
using namespace std;
enum class Tag{
no_tag,
p,
span,
it,
em
};
struct Part{
Part(Tag tag=Tag::no_tag, const std::string& s=""):tag(tag), s(s){}
Tag tag;
std::string s;
};
struct FormatterBase{
virtual string format(const Tag tag, const string& s)const=0;
virtual ~FormatterBase(){}
};
struct Formatter : public FormatterBase{
string format(Tag tag, const string& s)const override{
switch(tag){
case Tag::no_tag :
//std::cout << "format no_tag " << s << "\n";
return s;
case Tag::p :
//std::cout << "format p " << s << "\n";
return s + "\n";
case Tag::span :
//std::cout << "format span " << s << "\n";
return "<" + s + ">";
case Tag::strong :
//std::cout << "format it" << s << "\n";
return "*" + s + "*";
case Tag::em :
//std::cout << "format em " << s << "\n";
return "**" + s + "**";
}
}
};
struct MarkupStream : public ostream{
MarkupStream(const Formatter& fm, Tag tag=Tag::no_tag)
:_fm(fm)
,_parts({{tag, ""}}){}
template<class T>
MarkupStream& write(T& text){
stringstream s;
s << text ;
_parts.back().s += s.str();
return *this;
}
MarkupStream& concat(MarkupStream& stream){
_parts.back().s += stream.release_last_part();
return *this;
}
MarkupStream& operator() (MarkupStream& stream){
return concat(stream);
}
MarkupStream& operator ()(Tag tag){
_parts.emplace_back(tag, "");
return *this;
}
string release_last_part(){
auto& last = _parts.back();
auto res = _fm.format(last.tag, last.s);
_parts.pop_back();
return res;
}
const Formatter& _fm;
vector<Part> _parts;
};
template<class T>
MarkupStream& operator << (MarkupStream& stream, T text){
return stream.write(text);
}
MarkupStream& operator << (MarkupStream& lf, MarkupStream& rg){
return lf.concat(rg);
}
int main() {
Formatter fm;
MarkupStream s(fm);
s(Tag::p) << "Hello World! " << (s(Tag::span)(s(Tag::it) << 1 << 2)) << " " << (s(Tag::em) << "hi!");
cout << s.release_last_part();
return 0;
}
Here is the output of this code:
Hello World! <12> hi!