cemplateSimple C++ template engine |
git clone git://git.dimitrijedobrota.com/cemplate.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | cbe018c7ee9d7f35785c76694c55cf47ac288011 |
parent | 85c851c302432bd20e40604ab575ff6fd82dcc67 |
author | Dimitrije Dobrota <mail@dimitrijedobrota.com> |
date | Tue, 11 Mar 2025 18:39:28 +0100 |
Reinvent the library to be less verbose
Diffstat:M | BUILDING.md | | | ++++ |
M | CMakeLists.txt | | | +- |
M | CMakePresets.json | | | +- |
M | example/example.cpp | | | ++++++++++++++++++++++++++++++++++--------------------------- |
M | include/cemplate/cemplate.hpp | | | +++++++++++++++++++--------------------------------------------------------------- |
M | source/cemplate.cpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------- |
M | vcpkg.json | | | +- |
7 files changed, 185 insertions(+), 337 deletions(-)
diff --git a/BUILDING.md b/BUILDING.md
@@ -1,5 +1,9 @@
# Building with CMake
## Dependencies
For a list of dependencies, please refer to [vcpkg.json](vcpkg.json).
## Build
This project doesn't require any special command-line flags to build to keep
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -4,7 +4,7 @@ include(cmake/prelude.cmake)
project(
cemplate
VERSION 0.2.0
VERSION 0.3.0
DESCRIPTION "Simple C++ template engine"
HOMEPAGE_URL "https://git.dimitrijedobrota.com/cemplate.git"
LANGUAGES CXX
diff --git a/CMakePresets.json b/CMakePresets.json
@@ -10,7 +10,7 @@
"name": "dev-mode",
"hidden": true,
"cacheVariables": {
"package_DEVELOPER_MODE": "ON",
"cemplate_DEVELOPER_MODE": "ON",
"VCPKG_MANIFEST_FEATURES": "test"
}
},
diff --git a/example/example.cpp b/example/example.cpp
@@ -7,33 +7,40 @@ int main()
using namespace std::string_literals; // NOLINT
using namespace cemplate; // NOLINT
std::cout << Pragma("once") << '\n';
std::cout << Include("format");
std::cout << Include("iostream");
std::cout << Include("string");
std::cout << '\n';
std::cout << Namespace("cemplate");
std::cout << Function("test", "int");
std::cout << Return("3");
std::cout << Function("test");
std::cout << Function("test", "void", {{{"int"s, "val1"s}}});
std::cout << Function("test");
std::cout << Function(
"test", "void", {{"int"s, "val1"s}, {"std::string"s, "val2"s}});
std::cout << Function("test");
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 << Namespace("cemplate");
// clang-format off
// NOLINTBEGIN
program(std::cout,
Pragma("once"),
empty(),
Include("format"),
Include("iostream"),
Include("string"),
empty(),
Namespace("cemplate"),
Comment("some test function"),
Function("test", "int"),
Return("3"),
Function("test"),
Comment("another test function"),
Function("test", "void", {{{"int"s, "val1"s}}}),
Function("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"),
FunctionD("decl", "void"),
empty(),
"static const test_class test = ",
Initlist({"val11", "val12", {"val21", "val22"}, "val13"}),
Namespace("cemplate")
);
// NOLINTEND
// clang-format on
return 0;
}
diff --git a/include/cemplate/cemplate.hpp b/include/cemplate/cemplate.hpp
@@ -1,7 +1,9 @@
#pragma once
#include <algorithm>
#include <cstdint>
#include <functional>
#include <iostream>
#include <string>
#include <variant>
@@ -11,62 +13,89 @@ namespace cemplate
template<typename T>
using transform_f = std::function<std::string(const T&)>;
template<typename T>
std::string to_string(const T& val)
struct to_string
{
return val;
}
template<typename T>
std::string operator()(const T& val)
{
return static_cast<std::string>(val);
}
};
template<typename T>
std::string join(
const std::vector<T>& values,
const std::string& delim,
const transform_f<std::type_identity_t<T>>& transform = to_string<T>)
template<class InputIt, class UnaryFunc = to_string>
std::string join(InputIt first,
InputIt last,
const std::string& delim,
UnaryFunc func = to_string())
{
std::string res;
if (!values.empty()) {
res += transform(values[0]);
for (std::size_t i = 1; i < values.size(); i++) {
res += delim + transform(values[i]);
}
if (first == last) {
return res;
}
res += func(*first);
std::for_each(
++first, last, [&](const auto& val) { res += delim + func(val); });
return res;
}
class String
template<typename... Args>
void program(std::ostream& ost, const Args&... args)
{
public:
explicit String(std::string value)
: m_value(std::move(value))
{
}
((ost << to_string()(args)), ...);
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const String& rhs);
inline const char* empty()
{
return "\n";
}
private:
std::string m_value;
};
// 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);
std::string Namespace(const std::string& 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})); }
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})); }
std::string Statement(const std::string& content);
std::string Return(const std::string& value);
std::string Declaration(const std::string& type, const std::string& name, const std::string& value);
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 TemplateD(const std::string& var, const std::string& param) { return TemplateD(var, std::vector({param})); }
// NOLINTEND
// clang-format on
class InitlistElem;
class Initlist
{
public:
Initlist(std::initializer_list<InitlistElem> list);
template<typename InputItr, typename UnaryFunc>
Initlist(InputItr first, InputItr last, UnaryFunc func);
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)
{
values.emplace_back(std::forward<Args>(args)...);
}
std::vector<InitlistElem> values; // NOLINT
private:
std::vector<InitlistElem> m_values; // NOLINT
};
class InitlistElem
@@ -87,11 +116,6 @@ public:
{
}
InitlistElem(String value) // NOLINT
: m_value(std::move(value))
{
}
InitlistElem(std::initializer_list<InitlistElem> list) // NOLINT
: m_value(std::in_place_type<Initlist>, list)
{
@@ -108,6 +132,19 @@ private:
std::variant<std::string, Initlist> m_value;
};
inline Initlist::Initlist(std::initializer_list<InitlistElem> list)
: m_values(list)
{
}
template<typename InputItr, typename UnaryFunc>
Initlist::Initlist(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:
@@ -153,7 +190,6 @@ public:
const auto& params() const { return m_params; }
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const Function& rhs);
private:
std::string m_name;
@@ -179,231 +215,6 @@ public:
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const FunctionD& rhs);
};
class Call
{
public:
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);
protected:
const auto& func() const { return m_func; }
const auto& args() const { return m_args; }
private:
std::string m_func;
std::vector<std::string> m_args;
};
class Statement
{
public:
explicit Statement(std::string content)
: m_content(std::move(content))
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const Statement& rhs);
private:
std::string m_content;
};
class Template
{
public:
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 Template& rhs);
private:
std::vector<std::string> m_params;
};
class TemplateD
{
public:
TemplateD(std::string var, std::vector<std::string> params)
: m_var(std::move(var))
, 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 TemplateD& rhs);
private:
std::string m_var;
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 Namespace
{
public:
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 Return& rhs);
private:
std::string m_value;
};
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))
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const Declaration& rhs);
private:
std::string m_type;
std::string m_name;
std::string m_value;
};
template<typename T, int key>
std::ostream& operator<<(std::ostream& ost, const T& rhs)
{
return ost << static_cast<std::string>(rhs);
}
// 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,65 +29,100 @@ void warning(const std::string& message, const std::string& addition) // NOLINT
namespace cemplate
{
Pragma::operator std::string() const
std::string String(const std::string& value)
{
return std::format("#pragma {}\n", m_value);
return std::format(R"("{}")", value);
}
Include::operator std::string() const
std::string Pragma(const std::string& value)
{
return std::format("#include <{}>\n", m_header);
return std::format("#pragma {}\n", value);
}
IncludeL::operator std::string() const
std::string Include(const std::string& header)
{
return std::format("#include \"{}\"\n", m_header);
return std::format("#include <{}>\n", header);
}
Namespace::operator std::string() const
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() != m_name) {
if (seen.contains(m_name)) {
warning("nesting namespaces of the same name", m_name);
if (stk.empty() || stk.top() != name) {
if (seen.contains(name)) {
warning("nesting namespaces of the same name", name);
}
seen.insert(m_name);
stk.push(m_name);
seen.insert(name);
stk.push(name);
return std::format("namespace {}\n{{\n\n", m_name);
return std::format("namespace {}\n{{\n\n", name);
}
seen.erase(m_name);
seen.erase(name);
stk.pop();
return std::format("\n}} // namespace {}\n\n", m_name);
return std::format("\n}} // namespace {}\n\n", name);
}
std::string Comment(const std::string& value)
{
return std::format("{}// {}\n", indent(), value);
}
std::string MultilineComment(const std::vector<std::string>& values)
{
return std::format(
"{}/* {}\n*/\n",
indent(),
join(std::begin(values), std::end(values), "\n" + indent() + " "));
}
std::string Call(const std::string& func, const std::vector<std::string>& args)
{
return std::format(
"{}({})", func, join(std::begin(args), std::end(args), ", "));
}
Return::operator std::string() const
std::string Statement(const std::string& content)
{
return std::format("{}return {};\n", indent(), m_value);
return std::format("{}{};\n", indent(), content);
}
String::operator std::string() const
std::string Return(const std::string& value)
{
return std::format(R"("{}")", m_value);
return std::format("{}return {};\n", indent(), value);
}
Declaration::operator std::string() const
std::string Declaration(const std::string& type,
const std::string& name,
const std::string& value)
{
return std::format("{}{} {} = {};\n", indent(), m_type, m_name, m_value);
return std::format("{}{} {} = {};\n", indent(), type, name, value);
}
Call::operator std::string() const
std::string Requires(const std::string& value)
{
return std::format("{}({})", func(), join(args(), ", "));
return std::format("{}requires {}\n", indent(indent_lvl + 1), value);
}
Statement::operator std::string() const
std::string Template(const std::vector<std::string>& params)
{
return std::format("{}{};\n", indent(), m_content);
return std::format("{}template <{}>\n",
indent(),
join(std::begin(params), std::end(params), ", "));
}
std::string TemplateD(const std::string& var,
const std::vector<std::string>& params)
{
return std::format(
"{}<{}>", var, join(std::begin(params), std::end(params), ", "));
}
std::string Initlist::format(uint64_t lvl) const
@@ -106,7 +141,7 @@ std::string Initlist::format(uint64_t lvl) const
std::string res;
for (const auto& elem : values) {
for (const auto& elem : m_values) {
std::visit([&](const auto& val) { res += eval(val, lvl + 1); },
elem.value());
}
@@ -139,27 +174,18 @@ Function::operator std::string() const
last = name();
indent_lvl++;
return std::format("{} {}({})\n{{\n", ret(), name(), join(params(), ", "));
return std::format("{} {}({})\n{{\n",
ret(),
name(),
join(std::begin(params()), std::end(params()), ", "));
}
FunctionD::operator std::string() const
{
return std::format("{} {}({});\n", ret(), name(), join(params(), ", "));
}
Template::operator std::string() const
{
return std::format("{}template <{}>\n", indent(), join(m_params, ", "));
}
TemplateD::operator std::string() const
{
return std::format("{}<{}>", m_var, join(m_params, ", "));
}
Requires::operator std::string() const
{
return std::format("{}requires {}\n", indent(indent_lvl + 1), m_value);
return std::format("{} {}({});\n",
ret(),
name(),
join(std::begin(params()), std::end(params()), ", "));
}
} // namespace cemplate
diff --git a/vcpkg.json b/vcpkg.json
@@ -1,6 +1,6 @@
{
"name": "cemplate",
"version-semver": "0.2.0",
"version-semver": "0.3.0",
"dependencies": [],
"default-features": [],
"features": {