cemplate

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

commitb260b09c86df05ddd41655a108306c69cb2042f2
parent30ab0a2d363590ce35e1494537351d7cad520a98
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateTue, 25 Feb 2025 15:11:23 +0100

Fix naming and put everything in a class

Diffstat:
M.clang-tidy|+-
MCMakeLists.txt|+-
Mexample/example.cpp|+++++++++++++++---------------
Minclude/cemplate/cemplate.hpp|++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msource/cemplate.cpp|+++++++++++++++++++++++++++++++++-------------------------------------------------

5 files changed, 290 insertions(+), 167 deletions(-)


diff --git a/.clang-tidy b/.clang-tidy

@@ -57,7 +57,7 @@ CheckOptions:

- key: 'readability-identifier-naming.AbstractClassCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ClassCase'
value: 'lower_case'
value: 'CamelCase'
- key: 'readability-identifier-naming.ClassConstantCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ClassMemberCase'

diff --git a/CMakeLists.txt b/CMakeLists.txt

@@ -4,7 +4,7 @@ include(cmake/prelude.cmake)

project(
cemplate
VERSION 0.1.7
VERSION 0.1.8
DESCRIPTION "Simple C++ template engine"
HOMEPAGE_URL "https://git.dimitrijedobrota.com/cemplate.git"
LANGUAGES CXX

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

@@ -7,33 +7,33 @@ int main()

using namespace std::string_literals; // NOLINT
using namespace cemplate; // NOLINT
std::cout << pragma_once();
std::cout << Pragma("once") << '\n';
std::cout << include("format");
std::cout << include("iostream");
std::cout << include("string");
std::cout << Include("format");
std::cout << Include("iostream");
std::cout << Include("string");
std::cout << '\n';
std::cout << nspace("cemplate");
std::cout << Namespace("cemplate");
std::cout << func("test", "int");
std::cout << ret("3");
std::cout << func("test");
std::cout << Function("test", "int");
std::cout << Return("3");
std::cout << Function("test");
std::cout << func("test", "void", {{{"int"s, "val1"s}}});
std::cout << func("test");
std::cout << Function("test", "void", {{{"int"s, "val1"s}}});
std::cout << Function("test");
std::cout << func(
std::cout << Function(
"test", "void", {{"int"s, "val1"s}, {"std::string"s, "val2"s}});
std::cout << func("test");
std::cout << Function("test");
std::cout << func_decl("decl", "void");
std::cout << FunctionD("decl", "void");
std::cout << '\n';
std::cout << "static const test_class test = ";
std::cout << initlist({"val11", "val12", {"val21", "val22"}, "val13"});
std::cout << Initlist({"val11", "val12", {"val21", "val22"}, "val13"});
std::cout << nspace("cemplate");
std::cout << Namespace("cemplate");
return 0;
}

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

@@ -18,7 +18,7 @@ std::string to_string(const T& val)

}
template<typename T>
std::string accumulate(
std::string join(
const std::vector<T>& values,
const std::string& delim,
const transform_f<std::type_identity_t<T>>& transform = to_string<T>)

@@ -35,12 +35,15 @@ std::string accumulate(

return res;
}
struct initlist_elem;
class InitlistElem;
struct initlist
class Initlist
{
std::string format(std::uint64_t lvl) const;
friend std::ostream& operator<<(std::ostream& ost, const initlist& rhs);
public:
std::string format(uint64_t lvl) const;
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const Initlist& rhs);
template<typename... Args>
void emplace_back(Args... args)

@@ -48,32 +51,33 @@ struct initlist

values.emplace_back(std::forward<Args>(args)...);
}
std::vector<initlist_elem> values; // NOLINT
std::vector<InitlistElem> values; // NOLINT
};
struct initlist_elem
class InitlistElem
{
initlist_elem(std::string value) // NOLINT
public:
InitlistElem(std::string value) // NOLINT
: m_value(std::move(value))
{
}
initlist_elem(std::string_view value) // NOLINT
InitlistElem(std::string_view value) // NOLINT
: m_value(std::in_place_type<std::string>, value)
{
}
initlist_elem(const char* value) // NOLINT
InitlistElem(const char* value) // NOLINT
: m_value(value)
{
}
initlist_elem(std::initializer_list<initlist_elem> list) // NOLINT
: m_value(std::in_place_type<initlist>, list)
InitlistElem(std::initializer_list<InitlistElem> list) // NOLINT
: m_value(std::in_place_type<Initlist>, list)
{
}
initlist_elem(initlist list) // NOLINT
InitlistElem(Initlist list) // NOLINT
: m_value(std::move(list))
{
}

@@ -81,21 +85,20 @@ struct initlist_elem

const auto& value() const { return m_value; }
private:
std::variant<std::string, initlist> m_value;
std::variant<std::string, Initlist> m_value;
};
class func
class Function
{
public:
class param_t
struct param_t
{
public:
param_t(std::string type, std::string name)
: m_value(std::move(type) + " " + std::move(name))
{
}
explicit operator std::string() const { return m_value; }
operator std::string() const { return m_value; } // NOLINT
const auto& value() const { return m_value; }

@@ -103,30 +106,34 @@ public:

std::string m_value;
};
explicit func(std::string name)
explicit Function(std::string name)
: m_name(std::move(name))
{
}
func(std::string name, std::string ret, const std::vector<param_t>& params)
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())
{
}
func(std::string name, std::string ret, 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))
{
}
const auto& name() const { return m_name; }
const auto& ret() const { return m_ret; }
const auto& params() const { return m_params; }
friend std::ostream& operator<<(std::ostream& ost, const func& rhs);
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const Function& rhs);
private:
std::string m_name;

@@ -134,37 +141,44 @@ private:

std::vector<std::string> m_params;
};
class func_decl : public func
class FunctionD : public Function
{
public:
func_decl(std::string name,
FunctionD(std::string name,
std::string ret,
const std::vector<param_t>& params)
: func(std::move(name), std::move(ret), params)
: Function(std::move(name), std::move(ret), params)
{
}
func_decl(std::string name,
FunctionD(std::string name,
std::string ret,
std::vector<std::string> params = {})
: func(std::move(name), std::move(ret), std::move(params))
: Function(std::move(name), std::move(ret), std::move(params))
{
}
friend std::ostream& operator<<(std::ostream& ost, const func_decl& rhs);
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const FunctionD& rhs);
};
class call
class Call
{
public:
call(std::string func, std::string args)
Call(std::string func, std::vector<std::string> args)
: m_func(std::move(func))
, m_args(std::move(args))
{
}
Call(std::string func, std::string arg)
: m_func(std::move(func))
, m_args({std::move(arg)})
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const call& rhs);
friend std::ostream& operator<<(std::ostream& ost, const Call& rhs);
protected:
const auto& func() const { return m_func; }

@@ -172,79 +186,219 @@ protected:

private:
std::string m_func;
std::string m_args;
std::vector<std::string> m_args;
};
class call_s : public call
class Statement
{
public:
call_s(std::string func, std::string args)
: call(std::move(func), std::move(args))
explicit Statement(std::string content)
: m_content(std::move(content))
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const call_s& rhs);
friend std::ostream& operator<<(std::ostream& ost, const Statement& rhs);
private:
std::string m_content;
};
class tmplate
class Template
{
public:
explicit tmplate(std::vector<std::string> params)
explicit Template(std::vector<std::string> params)
: m_params(std::move(params))
{
}
explicit Template(std::string param)
: m_params({std::move(param)})
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const tmplate& rhs);
friend std::ostream& operator<<(std::ostream& ost, const Template& rhs);
private:
std::vector<std::string> m_params;
};
class tmplate_spec
class TemplateD
{
public:
explicit tmplate_spec(std::string var, std::string param)
TemplateD(std::string var, std::vector<std::string> params)
: m_var(std::move(var))
, m_param(std::move(param))
, m_params(std::move(params))
{
}
TemplateD(std::string var, std::string param)
: m_var(std::move(var))
, m_params({std::move(param)})
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const tmplate_spec& rhs);
friend std::ostream& operator<<(std::ostream& ost, const TemplateD& rhs);
private:
std::string m_var;
std::string m_param;
std::vector<std::string> m_params;
};
class Requires
{
public:
explicit Requires(std::string value)
: m_value(std::move(value))
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const Requires& rhs);
private:
std::string m_value;
};
class Pragma
{
public:
explicit Pragma(std::string value)
: m_value(std::move(value))
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const Pragma& rhs);
private:
std::string m_value;
};
class Include
{
public:
explicit Include(std::string header)
: m_header(std::move(header))
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const Include& rhs);
private:
std::string m_header;
};
class IncludeL
{
public:
explicit IncludeL(std::string header)
: m_header(std::move(header))
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const IncludeL& rhs);
private:
std::string m_header;
};
class rquires
class Namespace
{
public:
explicit rquires(std::string value)
explicit Namespace(std::string name)
: m_name(std::move(name))
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const Namespace& rhs);
private:
std::string m_name;
};
class Return
{
public:
explicit Return(std::string value)
: m_value(std::move(value))
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const rquires& rhs);
friend std::ostream& operator<<(std::ostream& ost, const Return& rhs);
private:
std::string m_value;
};
std::string pragma_once();
class Declaration
{
public:
Declaration(std::string type, std::string name, std::string value)
: m_type(std::move(type))
, m_name(std::move(name))
, m_value(std::move(value))
{
}
std::string include(const std::string& header, bool local = false);
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const Declaration& rhs);
std::string nspace(const std::string& name);
private:
std::string m_type;
std::string m_name;
std::string m_value;
};
std::string ret(const std::string& val);
class String
{
public:
explicit String(std::string value)
: m_value(std::move(value))
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const String& rhs);
private:
std::string m_value;
};
std::string string(const std::string& string);
template<typename T, int key>
std::ostream& operator<<(std::ostream& ost, const T& rhs)
{
return ost << static_cast<std::string>(rhs);
}
std::string decl(const std::string& type, const std::string& name);
// NOLINTBEGIN
#define DEF_OST(typename) \
inline std::ostream& operator<<(std::ostream& ost, const typename& rhs) \
{ \
return ost << static_cast<std::string>(rhs); \
}
// NOLINTEND
DEF_OST(Initlist)
DEF_OST(Function)
DEF_OST(FunctionD)
DEF_OST(Call)
DEF_OST(Statement)
DEF_OST(Template)
DEF_OST(TemplateD)
DEF_OST(Requires)
DEF_OST(Pragma)
DEF_OST(Include)
DEF_OST(IncludeL)
DEF_OST(Namespace)
DEF_OST(Return)
DEF_OST(Declaration)
DEF_OST(String)
} // namespace cemplate

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

@@ -29,168 +29,137 @@ void warning(const std::string& message, const std::string& addition) // NOLINT

namespace cemplate
{
std::string pragma_once()
Pragma::operator std::string() const
{
return "#pragma once\n\n";
return std::format("#pragma {}\n", m_value);
}
std::string include(const std::string& header, bool local)
Include::operator std::string() const
{
return local ? std::format("#include \"{}\"\n", header)
: std::format("#include <{}>\n", header);
return std::format("#include <{}>\n", m_header);
}
std::string nspace(const std::string& name)
IncludeL::operator std::string() const
{
return std::format("#include \"{}\"\n", m_header);
}
Namespace::operator std::string() const
{
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);
if (stk.empty() || stk.top() != m_name) {
if (seen.contains(m_name)) {
warning("nesting namespaces of the same name", m_name);
}
seen.insert(name);
stk.push(name);
seen.insert(m_name);
stk.push(m_name);
return std::format("namespace {}\n{{\n\n", name);
return std::format("namespace {}\n{{\n\n", m_name);
}
seen.erase(name);
seen.erase(m_name);
stk.pop();
return std::format("\n}} // namespace {}\n\n", name);
return std::format("\n}} // namespace {}\n\n", m_name);
}
std::string ret(const std::string& val)
Return::operator std::string() const
{
return std::format("{}return {};\n", indent(), val);
return std::format("{}return {};\n", indent(), m_value);
}
std::string string(const std::string& string)
String::operator std::string() const
{
return std::format(R"("{}")", string);
return std::format(R"("{}")", m_value);
}
std::string decl(const std::string& type, const std::string& name)
Declaration::operator std::string() const
{
return std::format("{}{} {} = ", indent(), type, name);
return std::format("{}{} {} = {};\n", indent(), m_type, m_name, m_value);
}
call::operator std::string() const
Call::operator std::string() const
{
return std::format("{}({})", func(), args());
return std::format("{}({})", func(), join(args(), ", "));
}
std::ostream& operator<<(std::ostream& ost, const call& rhs)
Statement::operator std::string() const
{
return ost << static_cast<std::string>(rhs);
return std::format("{}{};\n", indent(), m_content);
}
call_s::operator std::string() const
std::string Initlist::format(uint64_t lvl) const
{
return std::format("{}{}({});\n", indent(), func(), args());
}
std::ostream& operator<<(std::ostream& ost, const call_s& rhs)
{
return ost << static_cast<std::string>(rhs);
}
std::string eval(const std::string& val, std::uint64_t lvl); // NOLINT
std::string eval(const initlist& list, std::uint64_t lvl); // NOLINT
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>) {
return std::format(
"{}{{\n{}{}}},\n", indent(llvl), val.format(llvl + 1), indent(llvl));
} else {
return std::string();
}
};
std::string initlist::format(uint64_t lvl) const
{
std::string res;
for (const auto& node : values) {
std::visit([&](const auto& value) { res += eval(value, lvl + 1); },
node.value());
for (const auto& elem : values) {
std::visit([&](const auto& val) { res += eval(val, lvl + 1); },
elem.value());
}
return res;
}
std::string eval(const initlist& list, std::uint64_t lvl)
{
return std::format(
"{}{{\n{}{}}},\n", indent(lvl), list.format(lvl + 1), indent(lvl));
}
std::string eval(const std::string& val, std::uint64_t lvl)
{
return std::format("{}{},\n", indent(lvl), val);
}
std::ostream& operator<<(std::ostream& ost, const initlist& rhs)
Initlist::operator std::string() const
{
return ost << std::format(
"{{\n{}{}}};\n", rhs.format(indent_lvl + 1), indent());
return std::format("{{\n{}{}}};\n", format(indent_lvl + 1), indent());
}
std::string func_helper(const std::string& name, // NOLINT
const std::string& ret,
const std::vector<std::string>& params)
{
return std::format("{} {}({})", ret, name, accumulate(params, ", "));
}
std::ostream& operator<<(std::ostream& ost, const func& rhs)
Function::operator std::string() const
{
static std::string last;
if (last.empty()) {
if (rhs.ret().empty()) {
warning("function should have a return type", rhs.name());
if (!last.empty()) {
if (last != name()) {
warning("function is not closed", last);
}
last = rhs.name();
indent_lvl++;
return ost << func_helper(rhs.name(), rhs.ret(), rhs.params()) + "\n{\n";
last.clear();
indent_lvl--;
return "}\n\n";
}
if (last != rhs.name()) {
warning("function is not closed", last);
if (ret().empty()) {
warning("function should have a return type", name());
}
last.clear();
indent_lvl--;
return ost << "}\n\n";
}
std::ostream& operator<<(std::ostream& ost, const func_decl& rhs)
{
return ost << func_helper(rhs.name(), rhs.ret(), rhs.params()) + ";\n";
}
tmplate::operator std::string() const
{
return std::format("{}template <{}>\n", indent(), accumulate(m_params, ","));
last = name();
indent_lvl++;
return std::format("{} {}({})\n{{\n", ret(), name(), join(params(), ", "));
}
std::ostream& operator<<(std::ostream& ost, const tmplate& rhs)
FunctionD::operator std::string() const
{
return ost << static_cast<std::string>(rhs);
return std::format("{} {}({});\n", ret(), name(), join(params(), ", "));
}
tmplate_spec::operator std::string() const
Template::operator std::string() const
{
return std::format("{}<{}>", m_var, m_param);
return std::format("{}template <{}>\n", indent(), join(m_params, ", "));
}
std::ostream& operator<<(std::ostream& ost, const tmplate_spec& rhs)
TemplateD::operator std::string() const
{
return ost << static_cast<std::string>(rhs);
return std::format("{}<{}>", m_var, join(m_params, ", "));
}
rquires::operator std::string() const
Requires::operator std::string() const
{
return std::format("{}requires {}\n", indent(indent_lvl + 1), m_value);
}
std::ostream& operator<<(std::ostream& ost, const rquires& rhs)
{
return ost << static_cast<std::string>(rhs);
}
} // namespace cemplate