From 25c5de3653bc80e1860c00681f9663fe0763ab60 Mon Sep 17 00:00:00 2001 From: rpeschke Date: Fri, 3 Oct 2025 14:18:34 +0200 Subject: [PATCH] Proof of concept implementation of named function arguments --- examples/named_function_arguments.cpp2 | 35 ++ include/ntuples/bind_args.hh | 121 +++++ include/ntuples/comparators.hh | 35 ++ include/ntuples/constexpr_for.hh | 15 + include/ntuples/dataframe.hh | 131 ++++++ include/ntuples/full.hh | 33 ++ include/ntuples/generic_algorithms.hh | 130 ++++++ include/ntuples/groupby.hh | 126 ++++++ include/ntuples/groupby1.hh | 76 ++++ include/ntuples/join.hh | 43 ++ include/ntuples/macro_comperator.hh | 15 + include/ntuples/macro_groupby.hh | 11 + include/ntuples/macro_nt_field.hh | 99 ++++ include/ntuples/macro_nt_new_field.hh | 82 ++++ include/ntuples/nt_erased.hh | 219 +++++++++ include/ntuples/nt_vector_erased.hh | 214 +++++++++ include/ntuples/ntuples.hh | 599 +++++++++++++++++++++++++ include/ntuples/range.hh | 172 +++++++ include/ntuples/span.hh | 129 ++++++ include/ntuples/std_adapter.hh | 70 +++ include/ntuples/vector_frame.hh | 275 ++++++++++++ source/nt_fields_support.hh | 38 ++ source/to_cpp1.h | 6 + 23 files changed, 2674 insertions(+) create mode 100644 examples/named_function_arguments.cpp2 create mode 100644 include/ntuples/bind_args.hh create mode 100644 include/ntuples/comparators.hh create mode 100644 include/ntuples/constexpr_for.hh create mode 100644 include/ntuples/dataframe.hh create mode 100644 include/ntuples/full.hh create mode 100644 include/ntuples/generic_algorithms.hh create mode 100644 include/ntuples/groupby.hh create mode 100644 include/ntuples/groupby1.hh create mode 100644 include/ntuples/join.hh create mode 100644 include/ntuples/macro_comperator.hh create mode 100644 include/ntuples/macro_groupby.hh create mode 100644 include/ntuples/macro_nt_field.hh create mode 100644 include/ntuples/macro_nt_new_field.hh create mode 100644 include/ntuples/nt_erased.hh create mode 100644 include/ntuples/nt_vector_erased.hh create mode 100644 include/ntuples/ntuples.hh create mode 100644 include/ntuples/range.hh create mode 100644 include/ntuples/span.hh create mode 100644 include/ntuples/std_adapter.hh create mode 100644 include/ntuples/vector_frame.hh create mode 100644 source/nt_fields_support.hh diff --git a/examples/named_function_arguments.cpp2 b/examples/named_function_arguments.cpp2 new file mode 100644 index 000000000..a6bd7e2dd --- /dev/null +++ b/examples/named_function_arguments.cpp2 @@ -0,0 +1,35 @@ +#include "ntuples/full.hh" + + + + +template +auto variadic_template(Args&& ...args) -> void{ + auto t0 = bind_args( + @argument1 = 1, + @argument2 = 15, + @argument3 = nt::required(), + @argument4 = std::string("hello"))( + CPP2_FORWARD(args)...); + + std::cout << t0 << std::endl; +} + +auto make_ntuple(){ + return nt::ntuple{ + @a = 1, + @b = 2.5, + @c = std::string("three"), + @d = '4' + }; +} + +main: () = { + variadic_template( 1, 2.5, @argument4 = std::string("five") , @argument3 = 12 ); + + + nt1 := make_ntuple(); + + std::cout << nt1 << std::endl; +} + diff --git a/include/ntuples/bind_args.hh b/include/ntuples/bind_args.hh new file mode 100644 index 000000000..587e3c87f --- /dev/null +++ b/include/ntuples/bind_args.hh @@ -0,0 +1,121 @@ +#pragma once +#include "ntuples/ntuples.hh" +#include +namespace nt +{ + namespace __imple__ + { + + template + struct is_ax_name_container + { + private: + template + static std::true_type test(const nt::field_name_container *); + + static std::false_type test(...); + + public: + static constexpr bool value = decltype(test(std::declval *>()))::value; + }; + + template + constexpr bool is_ax_name_container_v = is_ax_name_container::value; + + + + template + auto bind_args1(NT_T &&t, T1 &&t1, ARGS &&...args) + { + if constexpr (::nt::__imple__::is_ax_name_container_v) + { + using T1Bare = std::remove_cvref_t; + if constexpr (T1Bare::template index_of() < N) + { + static_assert(dependent_false::value, "[NTUPLE ERROR] Named argument appears after positional arguments. " + "All named arguments must follow positional ones."); + } + else + { + + t[t1].v = t1.v; + + } + } + + if constexpr (sizeof...(args) == 0) + { + return t; + } + else + { + return ::nt::__imple__::bind_args1(t, std::forward(args)...); + } + } + + + template + auto bind_args0(NT_T &&t, T1 &&t1, ARGS &&...args) + { + if constexpr (is_ax_name_container_v) + { + return ::nt::__imple__::bind_args1(t, std::forward(t1), std::forward(args)...); + } + else + { + nt::get_nth(t).v = t1; + if constexpr (sizeof...(args) == 0) + { + return t; + } + else { + return ::nt::__imple__::bind_args0(t, std::forward(args)...); + } + } + } + + template + void check_optional_has_value(const T&) { + // Do nothing if T is not a specialization of std::optional + } + + template + void check_optional_has_value(const std::optional& opt) { + if (!opt.has_value()) { + throw std::runtime_error("std::optional is empty"); + } + } + + } + template + auto bind_args(NamedArgs &&...named_args) + { + // Return a lambda that captures named_args + return [&](auto &&...args) + { + auto tup = nt::ntuple(std::forward(named_args)...); + if constexpr (sizeof...(args) == 0) + { + constexpr_for<0, tup.__size__ , 1>([&](auto i){ + ::nt::__imple__::check_optional_has_value(get_nth(tup).v); + }); + + return tup; + } + else + { + + auto tup1 = ::nt::__imple__::bind_args0<0>(std::move(tup), std::forward(args)...); + + constexpr_for<0, tup1.__size__ , 1>([&](auto i){ + ::nt::__imple__::check_optional_has_value(get_nth(tup).v); + }); + return tup1; + } + }; + } + template + auto required(){ + return std::optional(); + } +} \ No newline at end of file diff --git a/include/ntuples/comparators.hh b/include/ntuples/comparators.hh new file mode 100644 index 000000000..a28bae7d6 --- /dev/null +++ b/include/ntuples/comparators.hh @@ -0,0 +1,35 @@ +#pragma once +#include "ntuples/ntuples.hh" + + +namespace nt::comparators +{ + + struct on_common_args_t + { + + template + constexpr static bool __comp__(T1 &&t1, T2 &&t2) + { + bool ret = true; + constexpr_for<0, _Remove_cvref_t::__size__, 1>( + [&](const auto i) + { + using N_th_T = _Remove_cvref_t(t1))>; + if constexpr (contains_type_v>) + { + ret &= (N_th_T::get(t1) == N_th_T::get(t2)); + } + }); + return ret; + } + + template + constexpr bool operator()(T1 &&t1, T2 &&t2) const + { + return __comp__(std::forward(t1), std::forward(t2)); + } + }; + constexpr inline on_common_args_t on_common_args; +} + diff --git a/include/ntuples/constexpr_for.hh b/include/ntuples/constexpr_for.hh new file mode 100644 index 000000000..035eaffb7 --- /dev/null +++ b/include/ntuples/constexpr_for.hh @@ -0,0 +1,15 @@ +#pragma once + +namespace nt{ + + template + constexpr void constexpr_for(F &&f) + { + if constexpr (Start < End) + { + f(std::integral_constant()); + constexpr_for(f); + } + } + +} \ No newline at end of file diff --git a/include/ntuples/dataframe.hh b/include/ntuples/dataframe.hh new file mode 100644 index 000000000..d9860f7c4 --- /dev/null +++ b/include/ntuples/dataframe.hh @@ -0,0 +1,131 @@ +#pragma once +#include "ntuples/ntuples.hh" + +namespace nt{ + + + + + template + struct dataframe : nt::base_maker_t, nt::ax_type2, typename Ts::struct_maker>>... + { + + template + decltype(auto) operator[](const nt::field_name_container &t) + { + return nt::field_name_container::get(*this); + } + + auto operator[](size_t i) + { + using ret_t = nt::ntuple<_Remove_cvref_t &...>; + return ret_t{ + get()[i]...}; + } + + auto operator[](size_t i) const + { + using ret_t = nt::ntuple; + return ret_t{ + get()[i]...}; + } + + template + void push_back(const T &t) + { + + [](auto...) {}(Ts::get(*this).emplace_back(Ts::get(t))...); + } + + template + void emplace_back(T &&...t) + { + static_assert(sizeof...(t) == sizeof...(Ts), "\n==============missmatched amount of arguments=================\n"); + [](auto...) {}(T::get(*this).emplace_back(std::forward(t))...); + } + + template + decltype(auto) get() const + { + return T1::get(*this); + } + + template + decltype(auto) get() + { + return T1::get(*this); + } + + auto size() const + { + auto size = _Remove_cvref_t>::get(*this).size(); + return size; + } + + template + static constexpr auto get_nth_type() + { + + return get_ax_name_container(NthTypeOf...>{}); + } + friend std::ostream &operator<<(std::ostream &out, const dataframe &self) + { + out << "|"; + + constexpr_for<0, sizeof...(Ts), 1>([&](auto ntuple_index) + { + static const auto x = self.template get_nth_type(); + out << " "; + out << std::setw(5) << x.get_name(); + out << " |"; }); + + out << "\n"; + out << "|"; + constexpr_for<0, sizeof...(Ts), 1>([&](auto i) + { + out << std::setw(5) << "-------|"; + }); + out << "\n"; + auto size = self.size(); + for (int i = 0; i < size; ++i) + { + auto current_element = self[i]; + out << "|"; + constexpr_for<0, sizeof...(Ts), 1>([&](auto ntuple_index) + { + static const auto x = self.template get_nth_type(); + out << " "; + out << std::setw(5) << x.get(current_element).v; + out << " |"; }); + out << "\n"; + } + + return out; + } + }; + + template + dataframe(Ts &&...ts) -> dataframe<_Remove_cvref_t...>; + + template + struct dataframe_maker + { + }; + + template + struct dataframe_maker> + { + using type = dataframe<_Remove_cvref_t...>; + }; + + template + auto fill_dataframe(int index, F &&f) + { + typename dataframe_maker::type ret; + for (int i = 0; i < index; ++i) + { + ret.push_back(f(i)); + } + return ret; + } +} \ No newline at end of file diff --git a/include/ntuples/full.hh b/include/ntuples/full.hh new file mode 100644 index 000000000..a0d98853d --- /dev/null +++ b/include/ntuples/full.hh @@ -0,0 +1,33 @@ +#pragma once + + + +#include "ntuples/ntuples.hh" + +#include "ntuples/ntuples.hh" +#include "ntuples/macro_nt_new_field.hh" +#include "ntuples/std_adapter.hh" + + + + +#include "ntuples/generic_algorithms.hh" +#include "ntuples/macro_nt_field.hh" +#include "ntuples/macro_comperator.hh" +#include "ntuples/macro_groupby.hh" +#include "ntuples/comparators.hh" +#include "ntuples/span.hh" +#include "ntuples/nt_erased.hh" +#include "ntuples/join.hh" +#include "ntuples/constexpr_for.hh" + +#include "ntuples/vector_frame.hh" + + + +#include "ntuples/bind_args.hh" +#include "ntuples/nt_vector_erased.hh" + +#include "ntuples/groupby1.hh" + +#include "ntuples/range.hh" \ No newline at end of file diff --git a/include/ntuples/generic_algorithms.hh b/include/ntuples/generic_algorithms.hh new file mode 100644 index 000000000..fa171f8da --- /dev/null +++ b/include/ntuples/generic_algorithms.hh @@ -0,0 +1,130 @@ +#pragma once + +namespace nt::algorithms +{ + + template + auto add_column(const VEC &vec, FUNC_T &&func) + { + std::vector ret; + ret.reserve(vec.size()); + for (const auto &e : vec) + { + ret.push_back(e | func(e)); + } + return ret; + } + + template + auto fill_vector(size_t entries, FUNC_T &&func) + { + std::vector ret; + ret.reserve(entries); + for (size_t i = 0; i < entries; ++i) + { + ret.push_back(func(i)); + } + return ret; + } + + template + std::ostream &operator<<(std::ostream &out, const std::vector> &self) + { + out << "|"; + + constexpr_for<0, sizeof...(ARGS), 1>([&](auto ntuple_index) + { + using T = nt::_Remove_cvref_t(self[0]))>; + out << ' '<([&](auto ntuple_index) + { + using T = nt::_Remove_cvref_t(self[0]))>; + out << "----|" ; + }); + + for (auto&& e : self){ + out << "\n"; + out << "|"; + constexpr_for<0, sizeof...(ARGS), 1>([&](auto ntuple_index) + { + + out << ' '<< nt::get_nth(e).v << " |" ; + }); + } + out << "\n"; + + return out; + } + + template + void join_vectors_r(T0 &ret, const T1 &t1, const T2 &t2, Comparision_T comp, projecttion_t project) + { + ret.clear(); + for (const auto &e1 : t1) + { + for (const auto &e2 : t2) + { + if (comp(e1, e2)) + { + ret.push_back(project(e1, e2)); + } + } + } + } + + template + auto join_vectors(const T1 &t1, const T2 &t2, Comparision_T comp, projecttion_t project) + { + std::vector ret; + join_vectors_r(ret, t1, t2, comp, project); + return ret; + } + + template + void sort(CONTAINER_T &container) + { + std::sort(container.begin(), container.end()); + } + + template + void sort(CONTAINER_T &container, COMP_T &&comp) + { + std::sort(container.begin(), container.end(), comp); + } + + template + auto count_if(const CONTAINER_T &container, OP_T op) + { + int i = 0; + for (const auto &e : container) + if (op(e)) + ++i; + return i; + } + + template + void filter(VEC_T &vec, FILTER_T &&f) + { + auto removeIt = std::remove_if(vec.begin(), vec.end(), [&](auto &&e) + { return !f(e); }); + vec.erase(removeIt, vec.end()); + } + + template + auto filter_copy(const VEC_T &vec, FILTER_T &&f) + { + VEC_T ret; + ret.reserve(vec.size()); + for (const auto &e : vec) + { + if (f(e)) + { + ret.push_back(e); + } + } + return ret; + } +} \ No newline at end of file diff --git a/include/ntuples/groupby.hh b/include/ntuples/groupby.hh new file mode 100644 index 000000000..f17e07921 --- /dev/null +++ b/include/ntuples/groupby.hh @@ -0,0 +1,126 @@ +#pragma once + +#include "ntuples/ntuples.hh" + + +namespace nt::algorithms +{ + + + + template + struct __group + { + template + static auto apply_append(const std::vector &vec, FUNC_T &&fun) + { + auto fun_c = [&](const auto& rng) + { + auto tail = rng.begin(); + return nt::ntuple(T::get(*tail)...) | fun(rng); + }; + return __apply__internal__(vec, fun_c); + } + + template + static auto apply(const std::vector &vec, FUNC_T &&fun) + { + auto fun_c = [&](const auto & rng) + { + return fun(rng); + }; + return __apply__internal__(vec, fun_c); + } + + private: + + template + static auto create_empty_vector() + { + return std::vector< decltype ( std::declval() ( std::declval& >() ) ) >{}; + } + + template + static auto __apply__internal__(const std::vector &vec, FUNC_T &&fun_c) + { + static constexpr auto lt = nt::comparators::lessThan(); + static constexpr auto eq = nt::comparators::equal(); + auto ret = create_empty_vector(); + + + + if (vec.empty()) + { + return ret; + } + auto min_element = vec[0]; + auto max_element = vec[0]; + + for (const auto& e : vec) { + if (lt(e, min_element)) { + min_element = e; + } + + if (lt(max_element, e)) { + max_element = e; + } + } + + if (eq(min_element, max_element)) { + ret.push_back( + fun_c(vec) + ); + return ret; + } + + std::vector buff{}; + buff.reserve(vec.size()); + + + + auto process = [&]() { + buff.clear(); + VEC_T next_min_element{}; + next_min_element = max_element; + + for (const auto& e : vec) { + if (eq(min_element, e)) { + buff.push_back(e); + } + else if (lt(min_element, e) && lt(e, next_min_element)) { + next_min_element = e; + } + + } + + min_element = next_min_element; + + + ret.push_back(fun_c(buff)); + }; + + while (lt(min_element, max_element)) { + process(); + + } + + process(); + + + return ret; + } + }; + + template + auto group(ARGS...) + { + return __group...>{}; + } + + + template + constexpr auto get_default_element(T&& t) { + return nt::_Remove_cvref_t{}; + } +} + diff --git a/include/ntuples/groupby1.hh b/include/ntuples/groupby1.hh new file mode 100644 index 000000000..4827deaff --- /dev/null +++ b/include/ntuples/groupby1.hh @@ -0,0 +1,76 @@ +#pragma once + +#include +#include +#include "ntuples/ntuples.hh" + +#include + +#include +#include +#include +#include + +namespace nt +{ + namespace __imple__{ + + struct group_IDS_T{ + size_t index = 0, group_ID = 0; + }; + } + + template + std::vector<::nt::__imple__::group_IDS_T> compute_group_ids_indexed(const Vecs &...keys) + { + using KeyT = std::tuple...>; + std::map seen; + std::vector<::nt::__imple__::group_IDS_T> result; + + size_t group_id = 0; + std::size_t N = std::get<0>(std::tie(keys...)).size(); + + for (std::size_t i = 0; i < N; ++i) + { + KeyT k{keys[i]...}; + auto [it, inserted] = seen.try_emplace(k, group_id); + if (inserted) + ++group_id; + result.emplace_back(i, it->second); + } + + std::ranges::sort(result, [](const auto &a, const auto &b) + { return a.group_ID < b.group_ID; }); + + return result; + } + + class GroupIndexBuffer : public std::vector< std::span > + { + const std::vector<::nt::__imple__::group_IDS_T> data; + //std::vector> spans; // [start, end) indices + + public: + template + + explicit GroupIndexBuffer(Vecs&&... keys ) + : data(compute_group_ids_indexed(std::forward(keys)...)) + { + if (data.empty()) + return; + + size_t start = 0; + for (size_t i = 1; i <= data.size(); ++i) + { + if (i == data.size() || data[i].group_ID != data[start].group_ID) + { + this->emplace_back(&data[start], i-start); + start = i; + } + } + } + + }; + + +} \ No newline at end of file diff --git a/include/ntuples/join.hh b/include/ntuples/join.hh new file mode 100644 index 000000000..9d78bcae9 --- /dev/null +++ b/include/ntuples/join.hh @@ -0,0 +1,43 @@ +#pragma once +#include + +namespace nt +{ + + template + auto join_on(nt::span s1, nt::span s2) + { + return std::pair, nt::span>(s1, s2); + } + + template + auto joinIndex(T... t) + { + + std::vector> ret; + + const auto &t1 = std::get<0>(std::tie(t...)); + const size_t rows1 = t1.first.size(); + const size_t rows2 = t1.second.size(); + + // Check if all sizes match + bool same_size = ((t.first.size() == rows1 && t.second.size() == rows2) && ...); + if (!same_size) + { + throw std::runtime_error("Mismatched input sizes"); + } + + for (size_t i = 0; i < rows1; ++i) + { + for (size_t j = 0; j < rows2; ++j) + { + + bool all_match = ((t.first[i] == t.second[j]) && ...); + if (all_match) + ret.emplace_back(i, j); + } + } + + return ret; + } +} \ No newline at end of file diff --git a/include/ntuples/macro_comperator.hh b/include/ntuples/macro_comperator.hh new file mode 100644 index 000000000..9c3716ebd --- /dev/null +++ b/include/ntuples/macro_comperator.hh @@ -0,0 +1,15 @@ +#pragma once +#include "ntuples/ntuples.hh" + + +#define nt_lessthan(...) [](){ using T = decltype( \ + nt::comparators::nt_compare( __VA_ARGS__ ) ); \ + static constexpr T t{}; \ + return [&](const auto& x,const auto &y) {return t.__isLessthen(x,y);}; }() + +#define nt_equal(...) [](){ using T = decltype( \ + nt::comparators::nt_compare( __VA_ARGS__ ) ); \ + static constexpr T t{}; \ + return [&](const auto& x,const auto &y) {return t.__isEequal(x,y);}; }() + + \ No newline at end of file diff --git a/include/ntuples/macro_groupby.hh b/include/ntuples/macro_groupby.hh new file mode 100644 index 000000000..d68aed539 --- /dev/null +++ b/include/ntuples/macro_groupby.hh @@ -0,0 +1,11 @@ +#pragma once + +#include "ntuples/groupby.hh" + + +#define nt_group(...) [](){ using T = decltype( \ + nt::algorithms::group( __VA_ARGS__ ) ); \ + static constexpr T t{}; \ + return t; }() + + \ No newline at end of file diff --git a/include/ntuples/macro_nt_field.hh b/include/ntuples/macro_nt_field.hh new file mode 100644 index 000000000..47dca17f7 --- /dev/null +++ b/include/ntuples/macro_nt_field.hh @@ -0,0 +1,99 @@ +#pragma once + +#include "ntuples/ntuples.hh" + + + +#define nt_field(field_name) []() constexpr { \ + auto struct_maker_template_lambda = [](auto e) constexpr { \ + using ARG_T = decltype(e); \ + if constexpr (e.N_value == nt::field_name_container_base_const::c_struct_maker) \ + { \ + if constexpr (!std::is_reference_v) \ + { \ + struct Zt##field_name \ + { \ + constexpr Zt##field_name() =default; \ + constexpr Zt##field_name(const decltype(e.val) &e_) : field_name(e_) {} \ + constexpr Zt##field_name(decltype(e.val) &e_) : field_name(e_) {} \ + constexpr Zt##field_name(decltype(e.val) &&e_) : field_name(std::move(e_)) {} \ + decltype(e.val) field_name; \ + decltype(e.val) value() const \ + { \ + return field_name; \ + } \ + }; \ + return Zt##field_name{}; \ + } \ + else \ + { \ + struct Zt##field_name \ + { \ + Zt##field_name(decltype(e.val) e_) : field_name(e_) {} \ + decltype(e.val) field_name; \ + decltype(e.val) value() const \ + { \ + return field_name; \ + } \ + }; \ + return Zt##field_name{}; \ + } \ + } \ + else if constexpr (e.N_value == nt::field_name_container_base_const::c_getter1) \ + { \ + struct getter_t \ + { \ + static constexpr decltype(auto) get(decltype(e.val) x) \ + { \ + return (x.field_name); \ + } \ + }; \ + return getter_t{}; \ + } \ + else if constexpr (e.N_value == nt::field_name_container_base_const::c_get_name) \ + { \ + struct name_getter_t \ + { \ + static constexpr const char* get_name() \ + { \ + return #field_name; \ + } \ + }; \ + return name_getter_t{}; \ + } \ + else if constexpr (e.N_value == nt::field_name_container_base_const::c_Static_assert_fail) \ + { \ + struct Static_assert_fail_T { \ + static constexpr void static_assert_fail(){ \ + static_assert(dependent_false::value, "[NTUPLE ERROR] Field `" #field_name "` does not exist in this ntuple"); \ + } \ + }; \ + return Static_assert_fail_T{}; \ + } \ + else if constexpr (e.N_value == nt::field_name_container_base_const::c_has_field) \ + { \ + struct has_field_t { \ + static constexpr bool has_field(){ \ + using T = std::remove_cvref_t; \ + if constexpr (requires (T& x) { x.field_name; }) { \ + return true; \ + } else { \ + return false; \ + } \ + } \ + }; \ + return has_field_t{}; \ + } \ + }; \ + return nt::field_name_container< \ + nt::field_name_container_base>{}; \ +}() + +#define nt_field_c(field_name, value) static constexpr inline auto field_name = (nt_field(field_name) = value) + +#define nt_field_t(field_name, value) \ + auto __internal__##field_name = [] { return nt_field(field_name) = value; }; \ + using field_name = decltype(__internal__##field_name()) + + + diff --git a/include/ntuples/macro_nt_new_field.hh b/include/ntuples/macro_nt_new_field.hh new file mode 100644 index 000000000..bba55bb72 --- /dev/null +++ b/include/ntuples/macro_nt_new_field.hh @@ -0,0 +1,82 @@ +#pragma once + +#include "ntuples/ntuples.hh" + + + + +#define __nt_new_field_core(field_name) \ + struct zt##field_name \ + { \ + template \ + struct base_t \ + { \ + constexpr base_t() {} \ + template \ + constexpr base_t(T1 &&e_) : \ + field_name(std::forward(e_)) {} \ + T field_name; \ + decltype(auto) value() const \ + { \ + return field_name; \ + } \ + }; \ + static auto get_name() \ + { \ + return #field_name; \ + } \ + template \ + static constexpr decltype(auto) get(T&& t) { \ + return (std::forward(t).field_name); \ + } \ + template \ + static constexpr decltype(auto) static_assert_fail() {\ + static_assert(dependent_false::value, \ + "[NTUPLE ERROR] Field `" #field_name \ + "` does not exist in this ntuple"); \ + } \ + template \ + static constexpr bool _is_containt_in(){ \ + using T = std::remove_reference_t; \ + if constexpr (requires (T& x) { x.field_name; }) { \ + return true; \ + } else { \ + return false; \ + } \ + } \ + } + +#define __nt_new_field(qualifier, field_name, value) \ + namespace __nt \ + { \ + __nt_new_field_core(field_name); \ + } \ + qualifier field_name = (nt::field_name_container<__nt::zt##field_name>{} = value) + +#define nt_new_field(field_name, value) __nt_new_field(static constexpr inline auto, field_name, value) +#define nt_new_field_c(field_name, value) __nt_new_field(static const inline auto, field_name, value) + +#define nt_new_field_t(field_name, value) \ + namespace __nt \ + { \ + __nt_new_field_core(field_name); \ + } \ + using field_name = decltype(nt::field_name_container<__nt::zt##field_name>{} = value) + +#define nt_new_field_name(field_name) \ + namespace __nt \ + { \ + __nt_new_field_core(field_name); \ + } \ + static constexpr inline auto field_name = nt::field_name_container<__nt::zt##field_name> {} + +#define nt_new_field_name_t(field_name) \ + namespace __nt \ + { \ + __nt_new_field_core(field_name); \ + } \ + using field_name = nt::field_name_container<__nt::zt##field_name> + + + + diff --git a/include/ntuples/nt_erased.hh b/include/ntuples/nt_erased.hh new file mode 100644 index 000000000..d8e2af84b --- /dev/null +++ b/include/ntuples/nt_erased.hh @@ -0,0 +1,219 @@ +#pragma once +#include + +#include +#include + +#include "ntuples/std_adapter.hh" +#include "ntuples/constexpr_for.hh" + +namespace nt +{ + + + template + struct is_ntuple : std::false_type {}; + + template + struct is_ntuple> : std::true_type {}; + template + concept ntuple_type = nt::is_ntuple>::value; + + + template + struct nt_erased + { + struct concept1 + { + virtual ~concept1() = default; + virtual std::type_index type_at(size_t index) const = 0; + virtual const void *get_raw(size_t index) const = 0; + + virtual std::type_index type_at(const std::string &name) const = 0; + virtual const void *get_raw(const std::string &name) const = 0; + virtual std::size_t size() const = 0; + virtual std::string get_name(size_t index) = 0; + + virtual T get(const std::string &name) const = 0; + virtual T get(size_t index) const = 0; + + }; + + template + struct model : concept1 + { + NT *data = nullptr; + + model(NT *d) : data(d) {} + + std::type_index type_at(size_t index) const override + { + std::type_index ret = typeid(int); + + constexpr_for<0, NT::__size__, 1>([&](auto i) + { + if (i == index) + { + ret = typeid(nt::_Remove_cvref_t((NT)*data).v)>); + } }); + + return ret; + } + + const void *get_raw(size_t index) const override + { + void *ret = nullptr; + + constexpr_for<0, NT::__size__, 1>([&](auto i) + { + if (i == index) + { + ret = &::nt::get(*data).v; + } }); + + return ret; + } + + virtual std::type_index type_at(const std::string &name) const override + { + std::type_index ret = typeid(int); + + constexpr_for<0, NT::__size__, 1>([&](auto i) + { + auto &ele = ::nt::get(*data); + if (ele.get_name() == name) + { + ret = typeid(nt::_Remove_cvref_t((NT)*data).v)>); + } }); + + return ret; + } + virtual const void *get_raw(const std::string &name) const override + { + void *ret = nullptr; + + constexpr_for<0, NT::__size__, 1>([&](auto i) + { + auto &ele = ::nt::get(*data); + if (ele.get_name() == name) + { + ret = &ele.v; + } }); + + return ret; + } + + virtual std::size_t size() const override + { + return NT::__size__; + } + + virtual std::string get_name(size_t index) { + std::string ret {}; + + constexpr_for<0, NT::__size__, 1>([&](auto i) + { + if (i == index) + { + ret = ::nt::get(*data).get_name(); + } }); + if (ret.empty()) + { + throw std::runtime_error("get_name: Could not find element at index: " + std::to_string(index)); + } + return ret; + } + + virtual T get(const std::string &name) const override + { + T ret = 0; + + constexpr_for<0, NT::__size__, 1>([&](auto i) + { + auto &ele = ::nt::get(*data); + if (ele.get_name() == name) + { + if constexpr (std::is_convertible_v) { + ret = static_cast(ele.v); + } else { + throw std::runtime_error("Value is not convertible to T"); + } + + } }); + + return ret; + } + + virtual T get(size_t index) const override + { + T ret = 0; + + constexpr_for<0, NT::__size__, 1>([&](auto i) + { + if (i == index) + { + auto& ele = ::nt::get(*data); + + if constexpr (std::is_convertible_v) { + ret = static_cast(ele.v); + } else { + throw std::runtime_error("Value is not convertible to T"); + } + } }); + + return ret; + } + + + }; + + std::shared_ptr self; + + template + nt_erased(NT &val) : self(std::make_shared>(&val)) {} + + nt_erased(const nt_erased &) = default; + nt_erased(nt_erased &) = default; + nt_erased(nt_erased &&) = default; + + + std::string get_name(size_t index) const { + return self->get_name(index); + } + auto type_at(size_t index) const{ + return self->type_at(index); + } + template + T1 get(size_t index) const + { + if (self->type_at(index) != typeid(T1)) + throw std::runtime_error("Type mismatch at index"); + return *reinterpret_cast(self->get_raw(index)); + } + + template + T1 get(const std::string &name) const + { + if (self->type_at(name) != typeid(T1)) + throw std::runtime_error("Type mismatch at index"); + return *reinterpret_cast(self->get_raw(name)); + } + + auto size() const + { + return self->size(); + } + + int get(const std::string &name) const + { + return self->get(name); + } + + int get(size_t index) const + { + return self->get(index); + } + + + }; +} \ No newline at end of file diff --git a/include/ntuples/nt_vector_erased.hh b/include/ntuples/nt_vector_erased.hh new file mode 100644 index 000000000..b6552adf1 --- /dev/null +++ b/include/ntuples/nt_vector_erased.hh @@ -0,0 +1,214 @@ +#pragma once +#include + +#include +#include +#include "ntuples/span.hh" +#include "ntuples/constexpr_for.hh" + +namespace nt +{ + + struct vector_erased + { + struct concept1 + { + virtual ~concept1() = default; + virtual std::size_t size() const = 0; + virtual const void *element_ptr(std::size_t i) const = 0; + virtual const void *element_ptr(const std::string &name) const = 0; + virtual std::size_t element_size() const = 0; + virtual std::size_t columns() const = 0; + virtual std::type_index type_at(std::size_t index) const = 0; + virtual std::type_index type_at(const std::string &name) const = 0; + virtual bool is_const_data() const = 0; + virtual std::string get_name(std::size_t index) const = 0; + }; + + template + struct model : concept1 + { + + VecT *vec; + + model(VecT *v) : vec(v) {} + + bool is_const_data() const override + { + return std::is_const_v>; + } + + std::size_t size() const override + { + return vec->size(); + } + + std::size_t columns() const override + { + + return (*vec)[0].__size__; + } + virtual std::string get_name(std::size_t index) const override { + std::string ret; + constexpr_for<0, nt::_Remove_cvref_t::__size__, 1>([&](auto i) + { + auto &ele = ::nt::get((*vec)[0]); + if (index == i) + { + ret = ele.get_name(); + } }); + + if (ret.empty()) + { + throw std::runtime_error("get_name: Could not find element at index: " + std::to_string(index)); + } + return ret; + } + const void *element_ptr(std::size_t index) const override + { + const void *ret = nullptr; + constexpr_for<0, nt::_Remove_cvref_t::__size__, 1>([&](auto i) + { + auto &ele = ::nt::get((*vec)[0]); + if (index == i) + { + ret = &(ele.v); + } }); + + if (ret == nullptr) + { + throw std::runtime_error("element_ptr: Could not find element at index: " + std::to_string(index)); + } + return ret; + } + + + + const void *element_ptr(const std::string &name) const override + { + const void *ret = nullptr; + constexpr_for<0, nt::_Remove_cvref_t::__size__, 1>([&](auto i) + { + auto &ele = ::nt::get((*vec)[0]); + if (ele.get_name() == name) + { + ret = &(ele.v); + } }); + if (ret == nullptr) { + throw std::runtime_error("element_ptr: Could not find element with name \"" + name + "\""); + } + return ret; + } + + std::size_t element_size() const override + { + return sizeof((*vec)[0]); + } + + std::type_index type_at(std::size_t index) const override + { + std::optional ret{}; + + constexpr_for<0, nt::_Remove_cvref_t::__size__, 1>([&](auto i) + { + auto &ele = ::nt::get((*vec)[0]); + if (index == i) + { + ret = typeid(nt::_Remove_cvref_t((*vec)[0]).v)>); + } }); + + + if (!ret.has_value()) + { + throw std::runtime_error("element_ptr: Could not find element at index: " + std::to_string(index)); + } + return *ret; + } + + virtual std::type_index type_at(const std::string &name) const override + { + std::optional ret{}; + + constexpr_for<0, nt::_Remove_cvref_t::__size__, 1>([&](auto i) + { + auto &ele = ::nt::get((*vec)[0]); + if (ele.get_name() == name) + { + ret = typeid(nt::_Remove_cvref_t((*vec)[0]).v)>); + } }); + if (!ret.has_value()) { + throw std::runtime_error("element_ptr: Could not find element with name \"" + name + "\""); + } + return *ret; + } + }; + + std::shared_ptr self; + + template + vector_erased(VecT &vec) : self(std::make_shared>(&vec)) {} + + + vector_erased(std::shared_ptr concept_) : self(std::move(concept_)) {} + + std::size_t size() const + { + return self->size(); + } + + + + std::size_t columns() const + { + return self->columns(); + } + + std::string column_name(std::size_t i){ + + return self->get_name(i); + } + + template + nt::span get(std::size_t i) const + { + if (self->is_const_data() && !std::is_const_v) + { + throw std::runtime_error("Cannot extract span with non-const T from const vector_erased"); + } + if (self->type_at(i) != typeid(T)){ + throw std::runtime_error("Type mismatch at index: " + std::to_string(i)); + } + + return nt::span( + reinterpret_cast(const_cast(self->element_ptr(i))), + self->size(), + self->element_size()); + } + + template + nt::span get(const std::string &name) const + { + if (self->is_const_data() && !std::is_const_v) + { + throw std::runtime_error("Cannot extract span with non-const T from const vector_erased"); + } + if (self->type_at(name) != typeid(T)){ + throw std::runtime_error("Type mismatch at element with name \"" + name + "\""); + } + + return nt::span( + reinterpret_cast(const_cast(self->element_ptr(name))), + self->size(), + self->element_size()); + } + std::type_index type_at(std::size_t index) const + { + return self->type_at(index); + }; + + std::type_index type_at(const std::string &name) const + { + return self->type_at(name); + }; + }; +} diff --git a/include/ntuples/ntuples.hh b/include/ntuples/ntuples.hh new file mode 100644 index 000000000..5f73407d2 --- /dev/null +++ b/include/ntuples/ntuples.hh @@ -0,0 +1,599 @@ +#pragma once + +#include + +#include +#include + +#include +#include + +#include "ntuples/constexpr_for.hh" + + + + +template +struct dependent_false : std::false_type +{ +}; + +namespace nt +{ + + template + using _Remove_cvref_t = std::remove_cv_t>; + + template + using NthTypeOf = + typename std::tuple_element>::type; + + template + struct type_container + { + using type = T1; + }; + + template + struct field_type : field_name_container_T_ + { + T1 v = {}; + constexpr field_type() =default; + constexpr field_type(const field_type&) = default; + constexpr field_type(field_type&&) = default; + constexpr field_type(T1 t1) : v(std::move(t1)) {} + constexpr field_type operator=(T1 &&t1) const { return field_type(std::move(t1)); } + constexpr field_type operator=(const T1 &t1) const { return field_type(t1); } + + template + constexpr field_type operator()(T_IN &&t1) const { return field_type(std::move(static_cast(t1))); } + + template + constexpr field_type operator()(const T_IN &t1) const { return field_type(static_cast(t1)); } + constexpr field_type operator=(T1 &&t1) + { + v = std::move(t1); + return *this; + } + constexpr field_type &operator=(const T1 &t1) + { + v = t1; + return *this; + } + + template + constexpr field_type &operator=(const field_type &t1) + { + v = t1.v; + return *this; + } + + using field_name_container_T = field_name_container_T_; + using data_t = T1; + + operator T1() + { + return v; + } + + operator const T1() const + { + return v; + } + + template + friend constexpr bool operator<(const field_type &lhs, const field_type &rhs) + { + return lhs.v < rhs.v; + } + + template + friend constexpr bool operator<=(const field_type &lhs, const field_type &rhs) + { + return lhs.v <= rhs.v; + } + + template + friend constexpr bool operator==(const field_type &lhs, const field_type &rhs) + { + return lhs.v == rhs.v; + } + + template + friend constexpr bool operator!=(const field_type &lhs, const field_type &rhs) + { + return lhs.v != rhs.v; + } + + template + friend constexpr bool operator>=(const field_type &lhs, const field_type &rhs) + { + return lhs.v >= rhs.v; + } + + template + friend constexpr bool operator>(const field_type &lhs, const field_type &rhs) + { + return lhs.v > rhs.v; + } + + friend constexpr bool operator<(const field_type &lhs, const T1 &rhs) + { + return lhs.v < rhs; + } + friend constexpr bool operator<(const T1 lhs, const field_type &rhs) + { + return lhs < rhs.v; + } + + friend constexpr bool operator<=(const field_type &lhs, const T1 &rhs) + { + return lhs.v <= rhs; + } + + friend constexpr bool operator<=(const T1 &lhs, const field_type &rhs) + { + return lhs <= rhs.v; + } + + friend constexpr bool operator==(const field_type &lhs, const T1 &rhs) + { + return lhs.v == rhs; + } + + friend constexpr bool operator==(const T1 &lhs, const field_type &rhs) + { + return lhs == rhs.v; + } + + friend constexpr bool operator!=(const field_type &lhs, const T1 &rhs) + { + return lhs.v != rhs; + } + + friend constexpr bool operator!=(const T1 &lhs, const field_type &rhs) + { + return lhs != rhs.v; + } + + friend constexpr bool operator>=(const field_type &lhs, const T1 &rhs) + { + return lhs.v >= rhs; + } + + friend constexpr bool operator>=(const T1 &lhs, const field_type &rhs) + { + return lhs >= rhs.v; + } + + friend constexpr bool operator>(const field_type &lhs, const T1 &rhs) + { + return lhs.v > rhs; + } + + friend constexpr bool operator>(const T1 &lhs, const field_type &rhs) + { + return lhs > rhs.v; + } + + friend std::ostream &operator<<(std::ostream &out, const field_type &self) + { + out << self.get_name() << ": "; + + if constexpr (requires(std::ostream &o, const decltype(self.v) &val) { o << val; }) + { + out << self.v; + } + else + { + out << ""; + } + return out; + } + + private: + using field_name_container_T_::operator=; + }; + + namespace comparators + { + template + struct _nt_compare + { + + template + static auto __isLessthen(const VECA_T &vecA, const VECB_T &vecB) + { + if constexpr (sizeof...(T_rest) > 0) + { + if (T1::get(vecA) < T1::get(vecB)) + { + return true; + } + else if (T1::get(vecA) > T1::get(vecB)) + { + return false; + } + return _nt_compare::template __isLessthen(vecA, vecB); + } + else + { + return T1::get(vecA) < T1::get(vecB); + } + } + + template + static auto __isEequal(const VECA_T &vecA, const VECB_T &vecB) + { + if constexpr (sizeof...(T_rest) > 0) + { + if (nt::_Remove_cvref_t::get(vecA) != nt::_Remove_cvref_t::get(vecB)) + { + return false; + } + return _nt_compare::template __isEequal(vecA, vecB); + } + else + { + return nt::_Remove_cvref_t::get(vecA) == nt::_Remove_cvref_t::get(vecB); + } + } + }; + + template + auto nt_compare(T_rest &&...) + { + return _nt_compare...>{}; + } + + template + constexpr auto lessThan(T_rest &&...) + { + return [less = _nt_compare...>{}](const auto &lhs, const auto &rhs) + { + return less.__isLessthen(lhs, rhs); + }; + } + + template + constexpr auto lessThan() + { + return [less = _nt_compare...>{}](const auto &lhs, const auto &rhs) + { + return less.__isLessthen(lhs, rhs); + }; + } + + template + constexpr auto equal(T_rest &&...) + { + return [less = _nt_compare...>{}](const auto &lhs, const auto &rhs) + { + return less.__isEequal(lhs, rhs); + }; + } + + template + constexpr auto equal() + { + return [less = _nt_compare...>{}](const auto &lhs, const auto &rhs) + { + return less.__isEequal(lhs, rhs); + }; + } + } + + template + struct ax_type2 : data_t_, field_name_container_T_ + { + + constexpr ax_type2() = default; + + using field_name_container_T = field_name_container_T_; + using data_t = data_t_; + }; + + struct field_name_container_base_const + { + static constexpr int c_struct_maker = 0; + static constexpr int c_getter1 = 1; + static constexpr int c_get_name = 2; + static constexpr int c_has_field = 3; + static constexpr int c_Static_assert_fail = 4; + }; + + template + struct field_name_container_base : field_name_container_base_const + { + using Lambda_T = T2; + + constexpr field_name_container_base() = default; + + template + struct type_wrap + { + static constexpr int N_value = N; + T val; + using type = T; + }; + + template + static constexpr auto struct_maker() + { + return decltype(std::declval()(std::declval>())){}; + } + + template + using base_t = decltype(struct_maker()); + + template + static constexpr decltype(auto) get(T &t) + { + using getter1 = decltype(std::declval()(std::declval>())); + + return getter1::get(t); + } + + template + static constexpr decltype(auto) get(const T &t) + { + using getter1 = decltype(std::declval()(std::declval>())); + return getter1::get(t); + } + + static constexpr decltype(auto) static_assert_fail() + { + using static_assert_fail_T = decltype(std::declval()(std::declval>())); + static_assert_fail_T::static_assert_fail(); + } + + template + static constexpr decltype(auto) get_value(const T &t) + { + return field_name_container_base::get(t).v; + } + + template + static constexpr decltype(auto) get_value(T &t) + { + return field_name_container_base::get(t).v; + } + + static constexpr decltype(auto) get_name() + { + using name_getter = decltype(std::declval()(std::declval>())); + return name_getter::get_name(); + } + template + static constexpr auto _is_containt_in(){ + using T = std::remove_reference_t; + using has_field_t = decltype(std::declval()(std::declval>())); + return has_field_t::has_field(); + } + }; + + + template + struct field_name_container : TBase + { + + template + inline static constexpr bool _is_containt_in_v =TBase:: template _is_containt_in(); + + template + constexpr static decltype(auto) get(T &&t) + { + + if constexpr (_is_containt_in_v) + { + return TBase::get(std::forward(t)); + } + else + { + TBase::static_assert_fail(); + } + } + + template + static constexpr decltype(auto) get_value(T &t) + { + return TBase::get(t).v; + } + + template + static constexpr decltype(auto) get_value(const T &t) + { + return TBase::get(t).v; + } + + template + constexpr auto operator=(T t) const + { + + return field_type<_Remove_cvref_t, field_name_container>{std::move(t)}; + } + + template + constexpr static auto has_field() + { + using BareT = std::remove_cvref_t; + return BareT::template contains_struct_maker_type>(); + } + template + constexpr static auto index_of() + { + using BareT = std::remove_cvref_t; + return BareT::template index_of_struct_maker_type>(); + } + }; + + template + field_name_container(Ts &&...ts) -> field_name_container<_Remove_cvref_t...>; + + template + auto constexpr get_ax_name_container(const field_name_container &t) + { + return field_name_container{}; + } + + template + struct base_maker + { + using type = typename T::template base_t; + }; + template + using base_maker_t = typename base_maker::type; + + template + using ntuple_base_t = base_maker_t<_Remove_cvref_t, T>; + + template + struct ntuple : ntuple_base_t... + { + constexpr ntuple(const ntuple &rhs) = default; + constexpr ntuple &operator=(const ntuple &rhs) = default; + constexpr ntuple(const T &...t1) : ntuple_base_t(t1)... {} + + constexpr ntuple() : ntuple_base_t(_Remove_cvref_t{})... {} + + template > + constexpr ntuple(Ts &&...t1) : ntuple_base_t(std::forward(t1))... {} + + template + decltype(auto) operator[](const field_name_container &t) + { + return field_name_container::get(*this); + } + + friend std::ostream &operator<<(std::ostream &out, const ntuple &self) + { + out << "|"; + + constexpr_for<0, sizeof...(T), 1>([&](auto i) + { + using current_t = decltype(NthTypeOf...>{}); + out << " "; + out << current_t::get(self); + out << " |"; }); + + return out; + } + + template + auto operator|(const ntuple &rhs) const + { + return ntuple( + T::get(*this)..., + ARGS::get(rhs)...); + } + + template + constexpr static bool contains_struct_maker_type() + { + return (([]() + { + using base_t = ntuple_base_t; + using maker_t = typename decltype(std::declval().value())::field_name_container_T; + return std::is_same_v; }()) || + ...); + } + +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wunused-value" +#elif defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-value" +#endif + + template + constexpr static int index_of_struct_maker_type() + { + int index = 0; + int result = -1; + // Fold expression over initializer list to evaluate in order and capture index + ((std::is_same_v>().value())::field_name_container_T, TARGET> + ? (result = index, false) + : true, + ++index), + ...); + return result; + } + +#if defined(__clang__) + #pragma clang diagnostic pop +#elif defined(__GNUC__) + #pragma GCC diagnostic pop +#endif + + friend constexpr bool operator<(const ntuple &lhs, const ntuple &rhs) + { + constexpr auto lt = comparators::lessThan(); + return lt(lhs, rhs); + } + inline static constexpr std::size_t __size__ = sizeof...(T); + }; + + template <> + struct ntuple<> + { + constexpr ntuple() = default; + constexpr ntuple(const ntuple &) = default; + constexpr ntuple &operator=(const ntuple &) = default; + + friend std::ostream &operator<<(std::ostream &out, const ntuple &self) + { + out << "nt::ntuple<>"; + + return out; + } + + template + auto operator|(const ntuple &rhs) const + { + return rhs; + } + + friend constexpr bool operator<(const ntuple &lhs, const ntuple &rhs) + { + + return false; + } + inline static constexpr std::size_t __size__ = 0; + }; + + template + ntuple(Ts &&...ts) -> ntuple<_Remove_cvref_t...>; + + template + constexpr decltype(auto) get_nth(const nt::ntuple &nt) + { + return nt::_Remove_cvref_t...>>::get(nt); + } + + template + constexpr decltype(auto) get_nth(nt::ntuple &nt) + { + return nt::_Remove_cvref_t...>>::get(nt); + } + + // Primary template for contains_type; defaults to false + template + struct contains_type : std::false_type + { + }; + + // Specialization for ntuple + template + struct contains_type> : std::disjunction...> + { + }; + + // Helper variable template + template + constexpr bool contains_type_v = contains_type::value; + +} + + + diff --git a/include/ntuples/range.hh b/include/ntuples/range.hh new file mode 100644 index 000000000..684d9c107 --- /dev/null +++ b/include/ntuples/range.hh @@ -0,0 +1,172 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace nt +{ + inline bool std_comp(void *v1, void *v2) + { + return v1 && (v1 != v2); + } +} + +namespace nt +{ + + + + template + class range + { + public: + inline constexpr static T* not_nulltptr = (T*)1; + struct iterator + { + + const T *ptr; + const range *base; + + using value_type = T; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + const value_type& operator*() const { return base->value; } + + iterator &operator++() + { + ptr = (T *)base->m_next((void *)ptr, (void *)base->to_, &(base->value) ); + return *this; + } + + iterator operator++(int) + { + auto tmp = *this; + ptr = (T *)base->m_next((void *)ptr, (void *)base->to_); + return tmp; + } + + bool operator==(const iterator &other) const { return !base->cmp((void *)ptr, (void *)base->to_); } + bool operator!=(const iterator &other) const { return base->cmp((void *)ptr, (void *)base->to_); } + }; + + range(const T *from, const T *to, T value_, + std::function next, + std::function cmp_ = nt::std_comp) : from_( from), to_(to), value(value_), m_next(std::move(next)), cmp(cmp_) + { + + } + range(range&& rhs) : from_(rhs.from_), to_(rhs.to_), value(rhs.value), m_next(std::move(rhs.m_next)), cmp(std::move(rhs.cmp) ) { + + } + range(const range& rhs) = delete; + + iterator begin() const + { + auto ret = iterator(); + ret.ptr = from_; + ret.base = this; + return ret; + } + iterator end() const + { + auto ret = iterator(); + ret.ptr = to_; + ret.base = this; + return ret; + } + + std::function m_next; + std::function cmp; + const T *from_; + const T *to_; + mutable T value = {}; + }; + + + template + auto subrange(const data_T &data, FUNC_T &&fun) + { + using vector_element_t = typename std::remove_cvref_t::value_type; + + auto *begin = [&](auto *vec) + { + while (vec != data.data() + data.size() && !fun(*vec)) + { + ++vec; + } + return vec; + }(data.data()); + + return ::nt::range{ + begin, + + data.data() + data.size(), + *begin, + [&](auto *v1, auto *v2, auto *v3) + { + double *ret = (vector_element_t *)v1; + double *end = (vector_element_t *)v2; + ++ret; + while (end != v2 && !fun(*ret)) + { + ++ret; + } + *(vector_element_t *)v3 = *ret; + return (void *)ret; + }}; + } + + template + auto generator(T begin_, T end, T step = 1) { + using element_t = typename std::remove_cvref_t; + return nt::range{ + nt::range::not_nulltptr, + nullptr, + begin_, + [&, end_ = end, current = begin_, step_ = step](auto *v1, auto *v2, auto *v3) mutable + { + current += step_; + void *ret = v1; + if (current == end_) + { + ret = nullptr; + } else { + *(element_t *)v3 = current; + } + + return (void *)ret; + }}; + }; + + + template + auto transform(const T &container, FUNC_T&& function) + { + using element_t = typename std::remove_cvref_t; + + auto begin = container.begin(); + auto first = function(*begin); + + return nt::range{ + nt::range::not_nulltptr, + nullptr, + first, + [&, current = begin, end_ = container.end(), function_ = std::forward(function)](auto *v1, auto *v2, auto *v3) mutable + { + ++current; + void *ret = v1; + if (current == end_) + { + ret = nullptr; + }else { + *(element_t *)v3 = function_(*current); + } + + return (void *)ret; + }}; + }; +} \ No newline at end of file diff --git a/include/ntuples/span.hh b/include/ntuples/span.hh new file mode 100644 index 000000000..45a90cda0 --- /dev/null +++ b/include/ntuples/span.hh @@ -0,0 +1,129 @@ +#pragma once + +#include +#include + +namespace nt +{ + template + class span : public std::ranges::view_interface> + { + public: + using element_type = T; + using value_type = std::remove_cv_t; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + class iterator + { + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + + iterator() : m_ptr(nullptr), m_stride(0) {} + iterator(std::byte *ptr, size_type stride) : m_ptr(ptr), m_stride(stride) {} + + reference operator*() const { return *reinterpret_cast(m_ptr); } + pointer operator->() const { return reinterpret_cast(m_ptr); } + + iterator &operator++() + { + m_ptr += m_stride; + return *this; + } + iterator operator++(int) + { + auto tmp = *this; + ++(*this); + return tmp; + } + + iterator &operator--() + { + m_ptr -= m_stride; + return *this; + } + iterator operator--(int) + { + auto tmp = *this; + --(*this); + return tmp; + } + + iterator operator+(difference_type n) const { return iterator(m_ptr + n * m_stride, m_stride); } + iterator operator-(difference_type n) const { return iterator(m_ptr - n * m_stride, m_stride); } + difference_type operator-(const iterator &other) const { return (m_ptr - other.m_ptr) / m_stride; } + + iterator &operator+=(difference_type n) + { + m_ptr += n * m_stride; + return *this; + } + iterator &operator-=(difference_type n) + { + m_ptr -= n * m_stride; + return *this; + } + + reference operator[](difference_type n) const { return *reinterpret_cast(m_ptr + n * m_stride); } + + bool operator==(const iterator &other) const { return m_ptr == other.m_ptr; } + bool operator!=(const iterator &other) const { return m_ptr != other.m_ptr; } + bool operator<(const iterator &other) const { return m_ptr < other.m_ptr; } + bool operator<=(const iterator &other) const { return m_ptr <= other.m_ptr; } + bool operator>(const iterator &other) const { return m_ptr > other.m_ptr; } + bool operator>=(const iterator &other) const { return m_ptr >= other.m_ptr; } + + private: + std::byte *m_ptr; + size_type m_stride; + }; + + span(T *ptr, size_type count, size_type stride = sizeof(T)) + : m_ptr((std::byte *)ptr), m_count(count), m_stride(stride) {} + + span() = default; + + operator nt::span() { return nt::span((const T *)m_ptr, m_count, m_stride); } + + reference operator[](size_type idx) const + { + return *reinterpret_cast(m_ptr + idx * m_stride); + } + + pointer data() const noexcept + { + return reinterpret_cast(m_ptr); + } + + size_type size() const noexcept + { + return m_count; + } + + size_type stride() const noexcept + { + return m_stride; + } + + iterator begin() const noexcept + { + return iterator(m_ptr, m_stride); + } + + iterator end() const noexcept + { + return iterator(m_ptr + m_count * m_stride, m_stride); + } + + private: + std::byte *m_ptr = nullptr; + size_type m_count = 0; + size_type m_stride = 0; + }; + +} diff --git a/include/ntuples/std_adapter.hh b/include/ntuples/std_adapter.hh new file mode 100644 index 000000000..d6ceff15e --- /dev/null +++ b/include/ntuples/std_adapter.hh @@ -0,0 +1,70 @@ +#pragma once +#include "ntuples.hh" +#include +#include + +namespace std { + + // Make nt::ntuple behave like std::tuple + + template + struct tuple_size> : std::integral_constant {}; + + template + struct tuple_element> { + using type = typename nt::NthTypeOf; + }; + +} // namespace std + +namespace nt { + + // std::get overloads for nt::ntuple + + template + decltype(auto) get(ntuple& t) { + + if constexpr (N >= sizeof...(Ts)) { + static_assert(dependent_false>::value, "[NTUPLE ERROR] get: index out of range (N must be less than the number of fields)."); + } else { + using Field = _Remove_cvref_t>; + return Field::get(t); + } + + } + + template + decltype(auto) get(const ntuple& t) { + if constexpr (N >= sizeof...(Ts)) { + static_assert(dependent_false>::value, "[NTUPLE ERROR] get: index out of range (N must be less than the number of fields)."); + + } else { + using Field = _Remove_cvref_t>; + return Field::get(t); + } + + } + + template + decltype(auto) get(ntuple&& t) { + if constexpr (N >= sizeof...(Ts)) { + static_assert(dependent_false>::value, "[NTUPLE ERROR] get: index out of range (N must be less than the number of fields)."); + } else { + using Field = _Remove_cvref_t>; + return Field::get(t); + } + + } + + template + decltype(auto) get(const ntuple&& t) { + if constexpr (N >= sizeof...(Ts)) { + static_assert(dependent_false>::value, "[NTUPLE ERROR] get: index out of range (N must be less than the number of fields)."); + } else { + using Field = _Remove_cvref_t>; + return Field::get(t); + } + + } + +} // namespace nt \ No newline at end of file diff --git a/include/ntuples/vector_frame.hh b/include/ntuples/vector_frame.hh new file mode 100644 index 000000000..ccde7395c --- /dev/null +++ b/include/ntuples/vector_frame.hh @@ -0,0 +1,275 @@ +#pragma once +#include "ntuples/ntuples.hh" +#include "ntuples/constexpr_for.hh" +#include "ntuples/generic_algorithms.hh" +#include +#include +#include +#include + + +namespace nt +{ + + namespace __imple__ + { + + template + struct member_span + { + member_span() + { + } + static constexpr std::size_t field_index = Index; + using M_FIELD_T = FIELD_T; + using M_VEC_T = VECT_T; + + nt::span get() + { + auto &m_vec = reinterpret_cast(*(this - field_index)); + + return nt::span( + m_vec.m_data.size() ? &(FIELD_T::get(m_vec.m_data[0])) : nullptr, + m_vec.m_data.size(), sizeof(decltype(m_vec.m_data[0]))); + } + + auto get_primitive() + { + auto &m_vec = reinterpret_cast(*(this - field_index)); + + return nt::span( + m_vec.m_data.size() ? &(FIELD_T::get(m_vec.m_data[0]).v) : nullptr, + m_vec.m_data.size(), sizeof(decltype(m_vec.m_data[0]))); + } + + auto operator()() + { + return get(); + } + + auto operator()() const + { + return get(); + } + + decltype(auto) operator[](size_t i) const + { + auto &m_vec = reinterpret_cast(*(this - field_index)); + return FIELD_T::get(m_vec.m_data[i]); + } + + decltype(auto) operator[](size_t i) + { + auto &m_vec = reinterpret_cast(*(this - field_index)); + return FIELD_T::get(m_vec.m_data[i]); + } + }; + + template + struct vector_frame_impl; + + template + struct vector_frame_impl, Ts...> : + + nt::base_maker_t< + nt::_Remove_cvref_t, + nt::ax_type2< + nt::__imple__::member_span, Ts...>, Is>, + typename Ts::field_name_container_T>>... + { + std::vector> m_data; + /* data */ + vector_frame_impl(const std::vector< + nt::ntuple> &vec) : m_data(vec) {} + + vector_frame_impl() = default; + friend auto begin(vector_frame_impl const &v) { return v.m_data.begin(); } + friend auto end(vector_frame_impl const &v) { return v.m_data.end(); } + + friend auto begin(vector_frame_impl &v) { return v.m_data.begin(); } + friend auto end(vector_frame_impl &v) { return v.m_data.end(); } + friend std::ostream &operator<<(std::ostream &os, const vector_frame_impl &v) + { + using nt::algorithms::operator<<; + os << v.m_data; + return os; + } + decltype(auto) operator[](std::size_t i) + { + return m_data[i]; + } + decltype(auto) operator[](std::size_t i) const + { + return m_data[i]; + } + std::size_t size() const + { + return m_data.size(); + } + }; + } + template + using vector_frame = nt::__imple__::vector_frame_impl, Ts...>; + + template + auto to_vector_frame(const std::vector> &vec) + { + return vector_frame(vec); + } + + namespace __imple__ + { + template + auto get_vector_frame_type(const nt::ntuple &vec) + { + return vector_frame(std::vector>{}); + } + } + + template + auto fill_vector_frame(size_t entries, FUNC_T &&func) + { + decltype(__imple__::get_vector_frame_type(func(size_t(0)))) ret; + ret.m_data.reserve(entries); + for (size_t i = 0; i < entries; ++i) + { + ret.m_data.push_back(func(i)); + } + return ret; + } + + template + auto fill_vector_frame(RANGE_T &&range_) + { + + decltype(__imple__::get_vector_frame_type(*range_.begin())) ret; + // ret.m_data.reserve(entries); + for (auto &&e : range_) + { + ret.m_data.push_back(e); + } + return ret; + } + + template + struct vector_frame_filler + { + + FUN_T m_fun; + template + vector_frame_filler(T &&f) : m_fun(f) {} + + auto operator()(size_t entries) + { + decltype(__imple__::get_vector_frame_type(m_fun(size_t(0)))) ret; + ret.m_data.reserve(ret.m_data.size() + entries); + for (size_t i = 0; i < entries; ++i) + { + ret.m_data.push_back(m_fun(i)); + } + return ret; + } + + template + auto operator()(size_t entries, std::vector &vec) + { + vec.reserve(vec.size() + entries); + for (size_t i = 0; i < entries; ++i) + { + vec.push_back(m_fun(i)); + } + } + + auto get_buffer() const + { + decltype(__imple__::get_vector_frame_type(m_fun(size_t(0)))) ret; + return ret; + } + }; + template + vector_frame_filler(F &&) -> vector_frame_filler>; + + template + void to_csv(const std::string &filename, const nt::vector_frame &vec) + { + auto out = std::ofstream(filename); + std::string start = ""; + nt::constexpr_for<0, sizeof...(ARGS), 1>([&](auto i) + { + using current_t = decltype(nt::NthTypeOf...>{}); + out << start << current_t::get_name(); + start = ","; }); + + out << "\n"; + for (auto &&e : vec) + { + start = ""; + nt::constexpr_for<0, sizeof...(ARGS), 1>([&](auto i) + { + using current_t = decltype(nt::NthTypeOf...>{}); + out << start << current_t::get(e).v; + start = ","; }); + out << "\n"; + } + } + + template + auto from_csv(const std::string &filename, nt::vector_frame &ret) + { + + auto in = std::ifstream(filename); + std::string line; + std::map header; + + std::vector dummy; + std::vector index_of_; + + if (!std::getline(in, line)) + { + return; + } + std::istringstream ss(line); + std::string token; + size_t i1 = 0; + while (std::getline(ss, token, ',')) + { + header[token] = i1++; + } + nt::constexpr_for<0, sizeof...(ARGS), 1>([&](auto index) + { + using current_t = decltype(nt::NthTypeOf...>{}); + auto it = header.find(current_t::get_name()); + if (it != header.end()) + { + index_of_.push_back(it->second); + } }); + size_t j = 0; + while (std::getline(in, line)) + { + size_t i = 0; + std::istringstream ss(line); + std::string token; + dummy.clear(); + while (std::getline(ss, token, ',')) + { + dummy.push_back(std::atof(token.c_str())); + } + + ret.m_data.emplace_back(); + + nt::constexpr_for<0, sizeof...(ARGS), 1>([&](auto index) + { + using current_t = decltype(nt::NthTypeOf...>{}); + current_t::get(ret.m_data[j]).v = dummy[index_of_[index]]; }); + + ++j; + } + } + + template + auto from_csv(const std::string &filename, nt::vector_frame &&ret) + { + from_csv(filename, ret); + return ret; + } +} \ No newline at end of file diff --git a/source/nt_fields_support.hh b/source/nt_fields_support.hh new file mode 100644 index 000000000..fb78b00dd --- /dev/null +++ b/source/nt_fields_support.hh @@ -0,0 +1,38 @@ +#pragma + +#include + +inline void replaceAtFields( std::string& input) { + if (input.find("@") == std::string::npos) { + return; + } + + std::string output; + output.reserve(input.size() * 2); // avoid reallocations + + for (size_t i = 0; i < input.size(); ++i) { + if (input[i] == '@') { + // start of identifier + size_t j = i + 1; + while (j < input.size() && + (std::isalnum(static_cast(input[j])) || input[j] == '_')) { + ++j; + } + + // Extract identifier + std::string name = input.substr(i + 1, j - (i + 1)); + + if (!name.empty()) { + output += "nt_field("; + output += name; + output += ")"; + i = j - 1; // skip past the identifier + continue; + } + } + // fallback: copy char as is + output.push_back(input[i]); + } + + input = output; +} \ No newline at end of file diff --git a/source/to_cpp1.h b/source/to_cpp1.h index ba23101d9..b222ba9e8 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -15,6 +15,7 @@ #include "sema.h" #include +#include "nt_fields_support.hh" namespace cpp2 { @@ -1213,6 +1214,11 @@ class cppfront { // Tokenize // + for (auto& e : source.get_lines()) { + replaceAtFields(e.text); + } + + tokens.lex(source.get_lines()); // Parse