cemplate

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

commit30ab0a2d363590ce35e1494537351d7cad520a98
parent51cc42e95b4ed60855abf0b74cf22fc1fb8a4c8f
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateMon, 24 Feb 2025 15:14:23 +0100

Classes with conversion op might be the future * Accumulate function as utility * Initlist constructor with string * Func and func_decl * Tmplate and tmplate_spec * Call and rquires

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

4 files changed, 250 insertions(+), 57 deletions(-)


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

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

project(
cemplate
VERSION 0.1.6
VERSION 0.1.7
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

@@ -4,6 +4,7 @@

int main()
{
using namespace std::string_literals; // NOLINT
using namespace cemplate; // NOLINT
std::cout << pragma_once();

@@ -15,14 +16,18 @@ int main()

std::cout << nspace("cemplate");
std::cout << func("test", "int", {});
std::cout << func("test", "int");
std::cout << ret("3");
std::cout << func("test");
std::cout << func("test", "void", {{"int", "val1"}, {"std::string", "val2"}});
std::cout << func("test", "void", {{{"int"s, "val1"s}}});
std::cout << func("test");
std::cout << func_decl("decl", "void", {});
std::cout << func(
"test", "void", {{"int"s, "val1"s}, {"std::string"s, "val2"s}});
std::cout << func("test");
std::cout << func_decl("decl", "void");
std::cout << '\n';
std::cout << "static const test_class test = ";

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

@@ -1,25 +1,46 @@

#pragma once
#include <cstdint>
#include <functional>
#include <string>
#include <variant>
#include <vector>
namespace cemplate
{
struct param_t
template<typename T>
using transform_f = std::function<std::string(const T&)>;
template<typename T>
std::string to_string(const T& val)
{
std::string type;
std::string name;
};
return val;
}
template<typename T>
std::string accumulate(
const std::vector<T>& values,
const std::string& delim,
const transform_f<std::type_identity_t<T>>& transform = to_string<T>)
{
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]);
}
}
return res;
}
struct initlist_elem;
struct initlist
{
std::string format(std::uint64_t lvl) const;
friend std::ostream& operator<<(std::ostream& ost, const initlist& list);
friend std::ostream& operator<<(std::ostream& ost, const initlist& rhs);
template<typename... Args>
void emplace_back(Args... args)

@@ -63,6 +84,157 @@ private:

std::variant<std::string, initlist> m_value;
};
class func
{
public:
class 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; }
const auto& value() const { return m_value; }
private:
std::string m_value;
};
explicit func(std::string name)
: m_name(std::move(name))
{
}
func(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 = {})
: 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);
private:
std::string m_name;
std::string m_ret;
std::vector<std::string> m_params;
};
class func_decl : public func
{
public:
func_decl(std::string name,
std::string ret,
const std::vector<param_t>& params)
: func(std::move(name), std::move(ret), params)
{
}
func_decl(std::string name,
std::string ret,
std::vector<std::string> params = {})
: func(std::move(name), std::move(ret), std::move(params))
{
}
friend std::ostream& operator<<(std::ostream& ost, const func_decl& rhs);
};
class call
{
public:
call(std::string func, std::string args)
: m_func(std::move(func))
, m_args(std::move(args))
{
}
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::string m_args;
};
class call_s : public call
{
public:
call_s(std::string func, std::string args)
: call(std::move(func), std::move(args))
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const call_s& rhs);
};
class tmplate
{
public:
explicit tmplate(std::vector<std::string> params)
: m_params(std::move(params))
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const tmplate& rhs);
private:
std::vector<std::string> m_params;
};
class tmplate_spec
{
public:
explicit tmplate_spec(std::string var, std::string param)
: m_var(std::move(var))
, m_param(std::move(param))
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const tmplate_spec& rhs);
private:
std::string m_var;
std::string m_param;
};
class rquires
{
public:
explicit rquires(std::string value)
: m_value(std::move(value))
{
}
operator std::string() const; // NOLINT
friend std::ostream& operator<<(std::ostream& ost, const rquires& rhs);
private:
std::string m_value;
};
std::string pragma_once();
std::string include(const std::string& header, bool local = false);

@@ -75,12 +247,4 @@ std::string string(const std::string& string);

std::string decl(const std::string& type, const std::string& name);
std::string func(const std::string& name,
const std::string& ret = "",
const std::vector<param_t>& params = {});
std::string func_decl(const std::string& name,
const std::string& ret = "",
const std::vector<param_t>& params = {});
} // namespace cemplate

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

@@ -1,5 +1,4 @@

#include <format>
#include <functional>
#include <iostream>
#include <stack>
#include <unordered_set>

@@ -25,23 +24,6 @@ void warning(const std::string& message, const std::string& addition) // NOLINT

std::cerr << '\n' << std::flush;
}
template<typename T>
std::string accumulate(const std::vector<T>& values,
const std::function<std::string(const T&)>& format,
const std::string& delim)
{
std::string res;
if (!values.empty()) {
res += format(values[0]);
for (std::size_t i = 1; i < values.size(); i++) {
res += delim + format(values[i]);
}
}
return res;
}
} // namespace
namespace cemplate

@@ -94,6 +76,26 @@ std::string decl(const std::string& type, const std::string& name)

return std::format("{}{} {} = ", indent(), type, name);
}
call::operator std::string() const
{
return std::format("{}({})", func(), args());
}
std::ostream& operator<<(std::ostream& ost, const call& rhs)
{
return ost << static_cast<std::string>(rhs);
}
call_s::operator std::string() 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

@@ -120,53 +122,75 @@ 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& list)
std::ostream& operator<<(std::ostream& ost, const initlist& rhs)
{
return ost << std::format(
"{{\n{}{}}};\n", list.format(indent_lvl + 1), indent());
"{{\n{}{}}};\n", rhs.format(indent_lvl + 1), indent());
}
std::string func_helper(const std::string& name, // NOLINT
const std::string& ret,
const std::vector<param_t>& params)
const std::vector<std::string>& params)
{
static const auto format = [](const param_t& param)
{ return param.name.empty() ? param.type : param.type + ' ' + param.name; };
return std::format(
"{} {}({})", ret, name, accumulate<param_t>(params, format, ", "));
return std::format("{} {}({})", ret, name, accumulate(params, ", "));
}
std::string func(const std::string& name,
const std::string& ret,
const std::vector<param_t>& params)
std::ostream& operator<<(std::ostream& ost, const func& rhs)
{
static std::string last;
if (last.empty()) {
if (ret.empty()) {
warning("function should have a return type", name);
if (rhs.ret().empty()) {
warning("function should have a return type", rhs.name());
}
last = name;
last = rhs.name();
indent_lvl++;
return func_helper(name, ret, params) + " {\n";
return ost << func_helper(rhs.name(), rhs.ret(), rhs.params()) + "\n{\n";
}
if (last != name) {
if (last != rhs.name()) {
warning("function is not closed", last);
}
last.clear();
indent_lvl--;
return "}\n\n";
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, ","));
}
std::ostream& operator<<(std::ostream& ost, const tmplate& rhs)
{
return ost << static_cast<std::string>(rhs);
}
tmplate_spec::operator std::string() const
{
return std::format("{}<{}>", m_var, m_param);
}
std::ostream& operator<<(std::ostream& ost, const tmplate_spec& rhs)
{
return ost << static_cast<std::string>(rhs);
}
rquires::operator std::string() const
{
return std::format("{}requires {}\n", indent(indent_lvl + 1), m_value);
}
std::string func_decl(const std::string& name,
const std::string& ret,
const std::vector<param_t>& params)
std::ostream& operator<<(std::ostream& ost, const rquires& rhs)
{
return func_helper(name, ret, params) + ";\n";
return ost << static_cast<std::string>(rhs);
}
} // namespace cemplate