Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Easy table definition with C++17/20 #482

Open
FunMiles opened this issue May 22, 2023 · 6 comments
Open

Easy table definition with C++17/20 #482

FunMiles opened this issue May 22, 2023 · 6 comments

Comments

@FunMiles
Copy link

I know this is sqlpp11. However one can use it within a C++17/20 context and exploits some of the features of these languages to further simplify the usage of sqlpp11.

@rbock does not like macros more than I do and the use of a separate preprocessor is not always adequate.
I have noted in the wishlist the desire for support for CREATE as well as the ideas of converting between SQL and C++ structs.

I decided to create table descriptions by hand, using a few helpers.

First, one can make use of fixed_string:

/// \brief A string class that can be used as template non-type argument.
template<std::size_t N>
struct fixed_string {
	constexpr fixed_string(const char (&str)[N]) {
		std::copy_n(str, N, data);
	}

	char data[N];
};

With the current implementation, one needs to be able to still obtain a character sequence from a fixed_string:

template <tools::fixed_string name, typename T>
struct make_char_sequence_impl;

template <tools::fixed_string s, std::size_t... i>
struct make_char_sequence_impl<s, sqlpp::detail::index_sequence<i...>>
{
	using type = sqlpp::char_sequence<s.data[i]...>;
};

template <tools::fixed_string name>
struct fs_char_sequence {
	using idx = sqlpp::detail::make_index_sequence<sizeof(name.data) - 1>;
	using type = typename make_char_sequence_impl<name, idx>::type;
};

To make a DRY version of column and table, a little utility that is used to define _alias_t. Here I am using structured binding to be able to obtain the data from a passed parent class template containing a single member:

template <tools::fixed_string name, template <typename> typename M>
struct alias_type {
	static constexpr const char *_literal = name.data;
	using _name_t = typename fs_char_sequence<name>::type;
	template<typename T>
	struct _member_t : M<T> {
		T &operator()() {
			auto &[r] = *this;
			return r;
		}
		const T &operator()() const {
			const auto &[r] = *this;
			return r;
		}
	};
};

And finally one can define column-declaration and table-declaration types:

template <tools::fixed_string name, template <typename T> typename Data, typename DataType, typename ... tags>
struct coldef {
	using _alias_t = alias_type<name, Data>;
	using _traits = sqlpp::make_traits<DataType, tags...>;
};

template <tools::fixed_string name, template <typename T> typename NamedTable, typename ... columns>
struct tabledef : sqlpp::table_t<tabledef<name,NamedTable, columns...>, columns...> {
	using _alias_t = alias_type<name,NamedTable>;
};

With these in place, providing columns and table definitions becomes simple with little boiler plate. First create templated structures that give a name to variables and use those to define both columns and tables:

template <typename T>
struct AlphaNamed {
	T alpha;
};
template <typename T>
struct DeltaNamed {
	T delta;
};
using AlphaColumn = coldef<"alpha", AlphaNamed, sqlpp::integral, sqlpp::tag::can_be_null>;
using DeltaColumn = coldef<"delta", DeltaNamed, sqlpp::varchar>;

template<typename T>
struct AlphaDeltaNamed {
	T alphaDelta;
};

using AlphaDeltaTable = tabledef<"alphaDelta", AlphaDeltaNamed, AlphaColumn, DeltaColumn>;

Now if only I could write:

   AlphaDeltaTable table{};
   db( create( table ).if_not_exist() );

Could this make it into sqlpp17/20? Nothing seems to have changed in a long while there and sqlpp11 is still touted as the stable version to use. Could make such tool make it into a future directory for users of more recent C++ version? (C++17 is now default on most compilers and the fixed_string only needs two more lines or so to work under C++17)

@rbock
Copy link
Owner

rbock commented Jun 4, 2023

Thanks for documenting your ideas in detail. I like the style you are proposing.

TBH, I am still hoping to get reflection and generation in C++ at some point. That would make sqlpp much easier to write, I think.

@FunMiles
Copy link
Author

FunMiles commented Jun 9, 2023

Thanks for documenting your ideas in detail. I like the style you are proposing.

Thanks

TBH, I am still hoping to get reflection and generation in C++ at some point. That would make sqlpp much easier to write, I think.

TBH I would love it as well, it would indeed make things incredibly easier and allow to express one's intention with barely more than a structure definition representing a table. Alas, even with a lot of optimism, we will have to wait quite a long time still before it happens, I think.

@rbock
Copy link
Owner

rbock commented Jun 10, 2023

Yeah, reflection and generation will take several years for sure.

As you may have seen, there is a version of sqlpp17, which is not too far from being usable, I believe. @Leon0402 is working on that in irregular intervals.

I also started sqlpp20 some time ago but gave up back in the day as the support for modules and concepts was very much incomplete back then.

That said: I won't invest a lot of time into a C++17/20 version myself, but I would applaud efforts in this direction :-)

@Leon0402
Copy link
Contributor

Very irregular intervals :/ As always so much other stuff to do. But I would love to find more time for sqlpp17. The only advantage is that by the time I brought sqlpp17 in shape, support for modules and that stuff will be available and we can port it to C++20 ;)

@FunMiles
Copy link
Author

I will start contributing to sqlpp17. @Leon0402: I have many other things to do and my needs of SQL are not quite of the most complex order, so my contributions might be limited to the sort I proposed earlier. As I noted, for what I propose to work under C++17, I think it should only need deduction guides for the fixed_string. Given that it is very little difference and does not impede anything C++20, it might be useful for C++17 already.
Would you start a C++20 branch? I feel that the concepts could already be used with positive benefits. For example, having concepts for type that can be cast to SQL types might already be useful. Concept support is very broad already with all the compilers I work with supporting it, but support for modules is still lagging.

@MeanSquaredError
Copy link
Contributor

@FunMiles Actually another nice improvement in C++20 is that it supports std::local_time and std::zoned_time which allows one to express more clearly the time zone of the written/read timestamps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants