cemplate

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

commit96cffb9ed1b87a545ad73a9f7f00fa3901e894b6
parentf63cf5a4a08eb6a1c9c106a0629d390d80ec5794
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateTue, 11 Mar 2025 20:13:46 +0100

More elegant way to use stateful functions

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

3 files changed, 106 insertions(+), 111 deletions(-)


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

@@ -7,6 +7,9 @@ int main()

using namespace std::string_literals; // NOLINT
using namespace cemplate; // NOLINT
Namespace nspace;
Function func;
// clang-format off
// NOLINTBEGIN
program(std::cout, {

@@ -16,20 +19,20 @@ int main()

Include("iostream"),
Include("string"),
empty(),
Namespace("cemplate"),
nspace.open("cemplate"),
Comment("some test function"),
Function("test", "int"),
func.open("test", "int"),
Return("3"),
Function("test"),
func.close("test"),
Comment("another test function"),
Function("test", "void", {{{"int"s, "val1"s}}}),
Function("test"),
func.open("test", "void", {{{"int"s, "val1"s}}}),
func.close("test"),
MultilineComment({"", "yet, another test function", "this time with multiple params"}),
Function( "test", "void", {{"int"s, "val1"s}, {"std::string"s, "val2"s}}),
Function("test"),
func.open( "test", "void", {{"int"s, "val1"s}, {"std::string"s, "val2"s}}),
func.close("test"),
FunctionD("decl", "void"),
empty(),

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

"static const test_class test = ",
Initlist({"val11", "val12", {"val21", "val22"}, "val13"}),
Namespace("cemplate")
nspace.close("cemplate")
});
// NOLINTEND
// clang-format on

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

@@ -4,7 +4,9 @@

#include <cstdint>
#include <functional>
#include <iostream>
#include <stack>
#include <string>
#include <unordered_set>
#include <variant>
namespace cemplate

@@ -41,6 +43,14 @@ std::string join(InputIt first,

return res;
}
template<typename T, class UnaryFunc = to_string>
std::string join(std::initializer_list<T> list,
const std::string& delim,
UnaryFunc func = to_string())
{
return join(std::begin(list), std::end(list), delim, func);
}
inline void program(std::ostream& ost, std::initializer_list<std::string> args)
{
std::for_each(

@@ -58,7 +68,6 @@ 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);
std::string Namespace(const std::string& name);
std::string Comment(const std::string& value);
std::string MultilineComment(const std::vector<std::string>& values);

@@ -74,9 +83,28 @@ std::string Declaration(const std::string& type, const std::string& name, const

std::string Requires(const std::string& value);
std::string Template(const std::vector<std::string>& params);
inline std::string Template(const std::string& param) { return Template(std::vector({param})); }
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})); }
struct param_t
{
param_t(std::string type, std::string name)
: m_value(std::move(type) + " " + std::move(name))
{
}
operator std::string() const { return m_value; } // NOLINT
const auto& value() const { return m_value; }
private:
std::string m_value;
};
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))); }
// NOLINTEND
// clang-format on

@@ -148,73 +176,34 @@ Initlist::Initlist(InputItr first, InputItr last, UnaryFunc func)

class Function
{
public:
struct param_t
{
param_t(std::string type, std::string name)
: m_value(std::move(type) + " " + std::move(name))
{
}
operator std::string() const { return m_value; } // NOLINT
const auto& value() const { return m_value; }
private:
std::string m_value;
};
explicit Function(std::string name)
: m_name(std::move(name))
{
}
Function(std::string name,
std::string ret,
const std::vector<param_t>& params)
: m_name(std::move(name))
, m_ret(std::move(ret))
, m_params(params.begin(), params.end())
{
}
std::string open(const std::string& name,
const std::string& ret,
const std::vector<std::string>& params = {});
Function(std::string name,
std::string ret,
std::vector<std::string> params = {})
: m_name(std::move(name))
, m_ret(std::move(ret))
, m_params(std::move(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)));
}
const auto& name() const { return m_name; }
const auto& ret() const { return m_ret; }
const auto& params() const { return m_params; }
operator std::string() const; // NOLINT
std::string close(const std::string& name);
private:
std::string m_name;
std::string m_ret;
std::vector<std::string> m_params;
std::string m_last;
};
class FunctionD : public Function
class Namespace
{
public:
FunctionD(std::string name,
std::string ret,
const std::vector<param_t>& params)
: Function(std::move(name), std::move(ret), params)
{
}
std::string open(const std::string& name);
std::string close(const std::string& name);
FunctionD(std::string name,
std::string ret,
std::vector<std::string> params = {})
: Function(std::move(name), std::move(ret), std::move(params))
{
}
operator std::string() const; // NOLINT
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

@@ -1,7 +1,5 @@

#include <format>
#include <iostream>
#include <stack>
#include <unordered_set>
#include "cemplate/cemplate.hpp"

@@ -49,27 +47,6 @@ std::string IncludeL(const std::string& header)

return std::format("#include \"{}\"\n", header);
}
std::string Namespace(const std::string& name)
{
static std::unordered_set<std::string> seen;
static std::stack<std::string> stk;
if (stk.empty() || stk.top() != name) {
if (seen.contains(name)) {
warning("nesting namespaces of the same name", name);
}
seen.insert(name);
stk.push(name);
return std::format("namespace {}\n{{\n\n", name);
}
seen.erase(name);
stk.pop();
return std::format("\n}} // namespace {}\n\n", name);
}
std::string Comment(const std::string& value)
{
return std::format("{}// {}\n", indent(), value);

@@ -125,6 +102,16 @@ std::string TemplateD(const std::string& var,

"{}<{}>", var, join(std::begin(params), std::end(params), ", "));
}
std::string FunctionD(const std::string& name,
const std::string& ret,
const std::vector<std::string>& params)
{
return std::format("{} {}({});\n",
ret,
name,
join(std::begin(params), std::end(params), ", "));
}
std::string Initlist::format(uint64_t lvl) const
{
const auto eval = []<typename T>(const T& val, std::uint64_t llvl)

@@ -154,38 +141,54 @@ Initlist::operator std::string() const

return std::format("{{\n{}{}}}", format(indent_lvl + 1), indent());
}
Function::operator std::string() const
std::string Function::open(const std::string& name,
const std::string& ret,
const std::vector<std::string>& params)
{
static std::string last;
if (!m_last.empty()) {
warning("opening, but function is not closed", m_last);
}
if (!last.empty()) {
if (last != name()) {
warning("function is not closed", last);
}
m_last = name;
indent_lvl++;
return std::format("{} {}({})\n{{\n",
ret,
name,
join(std::begin(params), std::end(params), ", "));
}
last.clear();
indent_lvl--;
return "}\n\n";
std::string Function::close(const std::string& name)
{
if (m_last != name) {
warning("closing, but function is not closed", m_last);
}
if (ret().empty()) {
warning("function should have a return type", name());
m_last.clear();
indent_lvl--;
return "}\n\n";
}
std::string Namespace::open(const std::string& name)
{
if (m_seen.contains(name)) {
warning("nesting namespaces of the same name", name);
}
last = name();
indent_lvl++;
return std::format("{} {}({})\n{{\n",
ret(),
name(),
join(std::begin(params()), std::end(params()), ", "));
m_seen.insert(name);
m_stk.push(name);
return std::format("namespace {}\n{{\n\n", name);
}
FunctionD::operator std::string() const
std::string Namespace::close(const std::string& name)
{
return std::format("{} {}({});\n",
ret(),
name(),
join(std::begin(params()), std::end(params()), ", "));
if (m_stk.empty() || m_stk.top() != name) {
warning("closing unproper namespace", name);
}
m_seen.erase(name);
m_stk.pop();
return std::format("\n}} // namespace {}\n\n", name);
}
} // namespace cemplate