cemplate

Simple C++ template engine
git clone git://git.dimitrijedobrota.com/cemplate.git
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING

commiteb546a84db538077491c0b5e54fcb70f4dc22915
parent96cffb9ed1b87a545ad73a9f7f00fa3901e894b6
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateWed, 12 Mar 2025 12:50:32 +0100

Centralization, no more global state

Diffstat:
Mexample/example.cpp|+++++++++++++++++++++++++++++----------------------------------
Minclude/cemplate/cemplate.hpp|+++++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msource/cemplate.cpp|++++++++++++++++++++++++++++++++++++++++++++++------------------------------------

3 files changed, 240 insertions(+), 224 deletions(-)


diff --git a/example/example.cpp b/example/example.cpp

@@ -7,42 +7,37 @@ int main()

using namespace std::string_literals; // NOLINT
using namespace cemplate; // NOLINT
Namespace nspace;
Function func;
Program program(std::cout);
// clang-format off
// NOLINTBEGIN
program(std::cout, {
Pragma("once"),
empty(),
Include("format"),
Include("iostream"),
Include("string"),
empty(),
nspace.open("cemplate"),
Comment("some test function"),
func.open("test", "int"),
Return("3"),
func.close("test"),
Comment("another test function"),
func.open("test", "void", {{{"int"s, "val1"s}}}),
func.close("test"),
MultilineComment({"", "yet, another test function", "this time with multiple params"}),
func.open( "test", "void", {{"int"s, "val1"s}, {"std::string"s, "val2"s}}),
func.close("test"),
FunctionD("decl", "void"),
empty(),
"static const test_class test = ",
Initlist({"val11", "val12", {"val21", "val22"}, "val13"}),
nspace.close("cemplate")
});
// NOLINTEND
program.pragma("once")
.line_empty()
.include("format")
.include("iostream")
.include("string")
.line_empty()
.namespace_open("cemplate")
.comment("some test function")
.function_open("test", "int")
.ret("3")
.function_close("test")
.comment("another test function")
.function_open("test", "void", {{{"int"s, "val1"s}}})
.function_close("test")
.multilineComment({"", "yet, another test function", "this time with multiple params"})
.function_open("test", "void", {{"int"s, "val1"s}, {"std::string"s, "val2"s}})
.function_close("test")
.function_decl("decl", "void")
.line_empty()
.line_value("static const test_class test = ")
.Initlist({"val11", "val12", {"val21", "val22"}, "val13"})
.namespace_close("cemplate");
// clang-format on
return 0;

diff --git a/include/cemplate/cemplate.hpp b/include/cemplate/cemplate.hpp

@@ -51,159 +51,159 @@ std::string join(std::initializer_list<T> list,

return join(std::begin(list), std::end(list), delim, func);
}
inline void program(std::ostream& ost, std::initializer_list<std::string> args)
class Program
{
std::for_each(
std::begin(args), std::end(args), [&](const auto& val) { ost << val; });
}
public:
using s_t = const std::string&;
using l_t = const std::vector<std::string>&;
inline const char* empty()
{
return "\n";
}
explicit Program(std::ostream& ost)
: m_ost(ost)
{
}
// clang-format off
// NOLINTBEGIN
std::string String(const std::string& value);
std::string Pragma(const std::string& value);
std::string Include(const std::string& header);
std::string IncludeL(const std::string& header);
struct param_t
{
param_t(s_t type, s_t name)
: m_value(type + " " + name)
{
}
std::string Comment(const std::string& value);
std::string MultilineComment(const std::vector<std::string>& values);
inline std::string MultilineComment(const std::string& value) { return MultilineComment(std::vector({value})); }
operator std::string() const { return m_value; } // NOLINT
std::string Call(const std::string& func, const std::vector<std::string>& args);
inline std::string Call(const std::string& func, const std::string& arg) { return Call(func, std::vector({arg})); }
const auto& value() const { return m_value; }
std::string Statement(const std::string& content);
std::string Return(const std::string& value);
private:
std::string m_value;
};
std::string Declaration(const std::string& type, const std::string& name, const std::string& value);
class InitlistElem;
std::string Requires(const std::string& value);
std::string Template(const std::vector<std::string>& params);
std::string TemplateD(const std::string& var, const std::vector<std::string>& params);
inline std::string Template(const std::string& param) { return Template(std::vector({param})); }
inline std::string TemplateD(const std::string& var, const std::string& param) { return TemplateD(var, std::vector({param})); }
class InitlistNode
{
public:
InitlistNode(std::initializer_list<InitlistElem> list);
struct param_t
{
param_t(std::string type, std::string name)
: m_value(std::move(type) + " " + std::move(name))
{
}
template<typename InputItr, typename UnaryFunc>
InitlistNode(InputItr first, InputItr last, UnaryFunc func);
operator std::string() const { return m_value; } // NOLINT
std::string format(uint64_t lvl) const;
const auto& value() const { return m_value; }
operator std::string() const; // NOLINT
private:
std::string m_value;
};
private:
std::vector<InitlistElem> m_values; // NOLINT
};
std::string FunctionD(const std::string& name, const std::string& ret, const std::vector<std::string>& params = {});
inline std::string FunctionD(const std::string& name, const std::string& ret, const std::vector<param_t>& params) { return FunctionD(name, ret, std::vector<std::string>(std::begin(params), std::end(params))); }
class InitlistElem
{
public:
InitlistElem(std::string value) // NOLINT
: m_value(std::move(value))
{
}
// NOLINTEND
// clang-format on
InitlistElem(std::string_view value) // NOLINT
: m_value(std::in_place_type<std::string>, value)
{
}
class InitlistElem;
InitlistElem(const char* value) // NOLINT
: m_value(value)
{
}
class Initlist
{
public:
Initlist(std::initializer_list<InitlistElem> list);
InitlistElem(std::initializer_list<InitlistElem> list) // NOLINT
: m_value(std::in_place_type<InitlistNode>, list)
{
}
template<typename InputItr, typename UnaryFunc>
Initlist(InputItr first, InputItr last, UnaryFunc func);
InitlistElem(InitlistNode list) // NOLINT
: m_value(std::move(list))
{
}
std::string format(uint64_t lvl) const;
const auto& value() const { return m_value; }
operator std::string() const; // NOLINT
private:
std::variant<std::string, InitlistNode> m_value;
};
private:
std::vector<InitlistElem> m_values; // NOLINT
};
// NOLINTBEGIN
Program& line_empty();
Program& line_value(s_t value);
Program& value(s_t value);
class InitlistElem
{
public:
InitlistElem(std::string value) // NOLINT
: m_value(std::move(value))
{
}
Program& string(s_t value);
Program& pragma(s_t value);
Program& include(s_t header);
Program& includeL(s_t header);
InitlistElem(std::string_view value) // NOLINT
: m_value(std::in_place_type<std::string>, value)
{
}
Program& comment(s_t value);
Program& multilineComment(l_t values);
InitlistElem(const char* value) // NOLINT
: m_value(value)
{
}
Program& call(s_t func, l_t args);
InitlistElem(std::initializer_list<InitlistElem> list) // NOLINT
: m_value(std::in_place_type<Initlist>, list)
{
}
Program& statement(s_t content);
Program& ret(s_t value);
Program& declaration(s_t type, s_t name, s_t value);
Program& require(s_t value);
Program& template_decl(l_t params);
Program& template_def(s_t var, l_t params);
Program& function_decl(s_t name, s_t ret, l_t params = {});
InitlistElem(Initlist list) // NOLINT
: m_value(std::move(list))
Program& function_open(s_t name, s_t ret, l_t params = {});
Program& function_open(s_t name, s_t ret, const std::vector<param_t>& params)
{
return function_open(
name,
ret,
std::vector<std::string>(std::begin(params), std::end(params)));
}
Program& function_close(s_t name);
Program& namespace_open(s_t name);
Program& namespace_close(s_t name);
Program& Initlist(const InitlistNode& node);
const auto& value() const { return m_value; }
// NOLINTEND
private:
std::variant<std::string, Initlist> m_value;
static auto indent(std::size_t lvl) { return std::string(lvl * 2, ' '); }
std::string indent() const { return indent(m_indent); }
std::size_t m_indent = 0;
// function
std::string m_function_last;
// namespace
std::unordered_set<std::string> m_namespace_seen;
std::stack<std::string> m_namespace_stack;
// ost
std::ostream& m_ost; // NOLINT
};
inline Initlist::Initlist(std::initializer_list<InitlistElem> list)
inline Program::InitlistNode::InitlistNode(
std::initializer_list<InitlistElem> list)
: m_values(list)
{
}
template<typename InputItr, typename UnaryFunc>
Initlist::Initlist(InputItr first, InputItr last, UnaryFunc func)
Program::InitlistNode::InitlistNode(InputItr first,
InputItr last,
UnaryFunc func)
{
m_values.reserve(last - first);
std::for_each(
first, last, [&](const auto& val) { m_values.emplace_back(func(val)); });
}
class Function
{
public:
std::string open(const std::string& name,
const std::string& ret,
const std::vector<std::string>& params = {});
std::string open(const std::string& name,
const std::string& ret,
const std::vector<param_t>& params)
{
return open(name,
ret,
std::vector<std::string>(std::begin(params), std::end(params)));
}
std::string close(const std::string& name);
private:
std::string m_last;
};
class Namespace
{
public:
std::string open(const std::string& name);
std::string close(const std::string& name);
private:
std::unordered_set<std::string> m_seen;
std::stack<std::string> m_stk;
};
} // namespace cemplate

diff --git a/source/cemplate.cpp b/source/cemplate.cpp

@@ -6,13 +6,6 @@

namespace
{
static std::uint64_t indent_lvl = 0; // NOLINT
auto indent(std::uint64_t lvl = indent_lvl)
{
return std::string(lvl * 2, ' ');
}
void warning(const std::string& message, const std::string& addition) // NOLINT
{
std::cerr << "Warning: " << message;

@@ -27,102 +20,127 @@ void warning(const std::string& message, const std::string& addition) // NOLINT

namespace cemplate
{
std::string String(const std::string& value)
Program& Program::line_empty()
{
return std::format(R"("{}")", value);
m_ost << "\n";
return *this;
}
std::string Pragma(const std::string& value)
Program& Program::line_value(s_t value)
{
return std::format("#pragma {}\n", value);
m_ost << indent() << value;
return *this;
}
std::string Include(const std::string& header)
Program& Program::value(s_t value)
{
return std::format("#include <{}>\n", header);
m_ost << value;
return *this;
}
std::string IncludeL(const std::string& header)
Program& Program::string(s_t value)
{
return std::format("#include \"{}\"\n", header);
m_ost << std::format(R"("{}")", value);
return *this;
}
std::string Comment(const std::string& value)
Program& Program::pragma(s_t value)
{
return std::format("{}// {}\n", indent(), value);
m_ost << std::format("#pragma {}\n", value);
return *this;
}
std::string MultilineComment(const std::vector<std::string>& values)
Program& Program::include(s_t header)
{
return std::format(
m_ost << std::format("#include <{}>\n", header);
return *this;
}
Program& Program::includeL(s_t header)
{
m_ost << std::format("#include \"{}\"\n", header);
return *this;
}
Program& Program::comment(s_t value)
{
m_ost << std::format("{}// {}\n", indent(), value);
return *this;
}
Program& Program::multilineComment(l_t values)
{
m_ost << std::format(
"{}/* {}\n*/\n",
indent(),
join(std::begin(values), std::end(values), "\n" + indent() + " "));
return *this;
}
std::string Call(const std::string& func, const std::vector<std::string>& args)
Program& Program::call(s_t func, l_t args)
{
return std::format(
m_ost << std::format(
"{}({})", func, join(std::begin(args), std::end(args), ", "));
return *this;
}
std::string Statement(const std::string& content)
Program& Program::statement(s_t content)
{
return std::format("{}{};\n", indent(), content);
m_ost << std::format("{}{};\n", indent(), content);
return *this;
}
std::string Return(const std::string& value)
Program& Program::ret(s_t value)
{
return std::format("{}return {};\n", indent(), value);
m_ost << std::format("{}m_ost << {};\n", indent(), value);
return *this;
}
std::string Declaration(const std::string& type,
const std::string& name,
const std::string& value)
Program& Program::declaration(s_t type, s_t name, s_t value)
{
return std::format("{}{} {} = {};\n", indent(), type, name, value);
m_ost << std::format("{}{} {} = {};\n", indent(), type, name, value);
return *this;
}
std::string Requires(const std::string& value)
Program& Program::require(s_t value)
{
return std::format("{}requires {}\n", indent(indent_lvl + 1), value);
m_ost << std::format("{}requires {}\n", indent(m_indent + 1), value);
return *this;
}
std::string Template(const std::vector<std::string>& params)
Program& Program::template_decl(l_t params)
{
return std::format("{}template <{}>\n",
indent(),
join(std::begin(params), std::end(params), ", "));
m_ost << std::format("{}template <{}>\n",
indent(),
join(std::begin(params), std::end(params), ", "));
return *this;
}
std::string TemplateD(const std::string& var,
const std::vector<std::string>& params)
Program& Program::template_def(s_t var, l_t params)
{
return std::format(
m_ost << std::format(
"{}<{}>", var, join(std::begin(params), std::end(params), ", "));
return *this;
}
std::string FunctionD(const std::string& name,
const std::string& ret,
const std::vector<std::string>& params)
Program& Program::function_decl(s_t name, s_t ret, l_t params)
{
return std::format("{} {}({});\n",
ret,
name,
join(std::begin(params), std::end(params), ", "));
m_ost << std::format("{} {}({});\n",
ret,
name,
join(std::begin(params), std::end(params), ", "));
return *this;
}
std::string Initlist::format(uint64_t lvl) const
std::string Program::InitlistNode::format(uint64_t lvl) const
{
const auto eval = []<typename T>(const T& val, std::uint64_t llvl)
{
if constexpr (std::is_same_v<T, std::string>) {
return std::format("{}{},\n", indent(llvl), val);
} else if (std::is_same_v<T, Initlist>) {
} else {
return std::format(
"{}{{\n{}{}}},\n", indent(llvl), val.format(llvl + 1), indent(llvl));
} else {
return std::string();
}
};

@@ -136,59 +154,62 @@ std::string Initlist::format(uint64_t lvl) const

return res;
}
Initlist::operator std::string() const
Program& Program::Initlist(const InitlistNode& node)
{
return std::format("{{\n{}{}}}", format(indent_lvl + 1), indent());
m_ost << std::format("{{\n{}{}}}", node.format(m_indent + 1), indent());
return *this;
}
std::string Function::open(const std::string& name,
const std::string& ret,
const std::vector<std::string>& params)
Program& Program::function_open(s_t name, s_t ret, l_t params)
{
if (!m_last.empty()) {
warning("opening, but function is not closed", m_last);
if (!m_function_last.empty()) {
warning("opening, but function is not closed", m_function_last);
}
m_last = name;
indent_lvl++;
return std::format("{} {}({})\n{{\n",
ret,
name,
join(std::begin(params), std::end(params), ", "));
m_function_last = name;
m_indent++;
m_ost << std::format("{} {}({})\n{{\n",
ret,
name,
join(std::begin(params), std::end(params), ", "));
return *this;
}
std::string Function::close(const std::string& name)
Program& Program::function_close(s_t name)
{
if (m_last != name) {
warning("closing, but function is not closed", m_last);
if (m_function_last != name) {
warning("closing, but function is not closed", m_function_last);
}
m_last.clear();
indent_lvl--;
return "}\n\n";
m_function_last.clear();
m_indent--;
m_ost << "}\n\n";
return *this;
}
std::string Namespace::open(const std::string& name)
Program& Program::namespace_open(s_t name)
{
if (m_seen.contains(name)) {
if (m_namespace_seen.contains(name)) {
warning("nesting namespaces of the same name", name);
}
m_seen.insert(name);
m_stk.push(name);
m_namespace_seen.insert(name);
m_namespace_stack.push(name);
return std::format("namespace {}\n{{\n\n", name);
m_ost << std::format("namespace {}\n{{\n\n", name);
return *this;
}
std::string Namespace::close(const std::string& name)
Program& Program::namespace_close(s_t name)
{
if (m_stk.empty() || m_stk.top() != name) {
if (m_namespace_stack.empty() || m_namespace_stack.top() != name) {
warning("closing unproper namespace", name);
}
m_seen.erase(name);
m_stk.pop();
return std::format("\n}} // namespace {}\n\n", name);
m_namespace_seen.erase(name);
m_namespace_stack.pop();
m_ost << std::format("\n}} // namespace {}\n\n", name);
return *this;
}
} // namespace cemplate