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

Backport C++20 concepts to C++17 in the 'sparqlExpressions' module #1787

Merged

Conversation

krzysztof-tyb
Copy link
Contributor

@krzysztof-tyb krzysztof-tyb commented Feb 10, 2025

The backport is implemented using macros from Eric Niebler's range-v3 library.

@krzysztof-tyb
Copy link
Contributor Author

Hi @joka921

Here is the sparqlExpressions part of backporting concepts to C++17.
I didn't find a better solution for concepts used in template lambda. For this reason, I used the CPP_assert macro to handle them inside lambda.

ValueGetter&& valueGetter) {
CPP_assert(SingleExpressionResult<Input>);
auto transformation = [context, valueGetter]<typename I>(I&& i)
-> CPP_ret(decltype(valueGetter(AD_FWD(i), context)))(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason, clang17 and older cause the compiler to crash if the return type is explicitly defined in nested lambda.
Clang18 compiles it correctly.
It looks like a bug in older compilator version.

auto transformation = [context, valueGetter]<typename I>(I&& i)
-> CPP_ret(decltype(valueGetter(AD_FWD(i), context)))(
requires ranges::invocable<ValueGetter, I&&, EvaluationContext*>) {
inline auto valueGetterGenerator = CPP_lambda()(
Copy link
Contributor Author

@krzysztof-tyb krzysztof-tyb Feb 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joka921
To solve the clang crash for lambdas mentioned previously I added the CPP_lambda macro which expands to requires in C++20 and std::enable_if_t (as additional lambda argument) in C++17.
What do you think about it? It can be used instead of CPP_assert in other lambdas.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I like the CPP_lambda macro, feel free to use it in the other places,
but the CPP_asserts are also fine in the many places where there is no overload resolution happening.

Copy link
Member

@joka921 joka921 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice, I have some suggestions and some open questions, partly also for myself.
I suggest that you read my comments and make another pass with the things you think are appropriate, and then you ping me again and I make a final pass with the small cleanups and the merging together etc.

#define CPP_LAMBDA_AUX_WHICH_(FIRST, ...) CPP_PP_EVAL(CPP_PP_CHECK, FIRST)

#define CPP_LAMBDA_AUX_0(...) __VA_ARGS__

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea very much, I also have added some additional macros in another branch and will consolidate them when merging (in particular I have opened a several file for the internal SFINAE_AUX_WHICH_BLA_... macros.

Comment on lines 1486 to 1487
CPP_assert(sparqlExpression::SingleExpressionResult<T> &&
VectorOfAggregationData<A>);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest two separate CPP_assert() for &&, because then you get better diagnostics in the failure case.

Comment on lines 77 to 78
&evaluationContext]<sparqlExpression::SingleExpressionResult T>(
[blockSize, &evaluationContext]<typename T>(
VectorOfAggregationData auto& aggregateData, T&& singleResult) {
CPP_assert(sparqlExpression::SingleExpressionResult<T>);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that you have it, you can consider rewriting the CPP-asser with CPP_lambda ,but I am also fine with the asserts in places where we don't need overload resolution. Just let me know how you decide.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to use CPP_lambda instead of CPP_assert but not all. I keep CPP_assert where we have template parameter packs at this moment. We need to somehow deal with the lambda explicit template parameter list to be compatible with C++17.

Comment on lines +59 to +61
if constexpr (ranges::invocable<decltype(aggregateOperation._function),
decltype(AD_FWD(x)),
decltype(AD_FWD(y))>) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you give the X and Y names in the lambda declaration, then this is
ranges::invocable<AggregateOperation, X&&, Y&&>

This is more readable then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean X, and Y as lambda template parameters?
However, the problem is that the lambda explicit template parameter list has been available since C++20.
So later we need to somehow deal with this to be compatible with C++17.

auto transformation = [context, valueGetter]<typename I>(I&& i)
-> CPP_ret(decltype(valueGetter(AD_FWD(i), context)))(
requires ranges::invocable<ValueGetter, I&&, EvaluationContext*>) {
inline auto valueGetterGenerator = CPP_lambda()(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I like the CPP_lambda macro, feel free to use it in the other places,
but the CPP_asserts are also fine in the many places where there is no overload resolution happening.


// Ensures that T is a floating-point type.
template <typename T>
CPP_concept FloatingPoint = std::is_floating_point_v<T>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still have to figure out where to put them and what to name them.

I think I already have a file src/backports/concepts.h where I want a common namespace for the concepts that are in C++20.

Copy link

codecov bot commented Feb 12, 2025

Codecov Report

Attention: Patch coverage is 96.68508% with 6 lines in your changes missing coverage. Please review.

Project coverage is 90.03%. Comparing base (1570033) to head (5251f9e).
Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
src/engine/Filter.cpp 89.74% 0 Missing and 4 partials ⚠️
...engine/sparqlExpressions/RelationalExpressions.cpp 75.00% 1 Missing ⚠️
...ine/sparqlExpressions/SparqlExpressionGenerators.h 96.77% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1787      +/-   ##
==========================================
+ Coverage   90.02%   90.03%   +0.01%     
==========================================
  Files         396      396              
  Lines       37974    37989      +15     
  Branches     4262     4262              
==========================================
+ Hits        34185    34203      +18     
+ Misses       2493     2491       -2     
+ Partials     1296     1295       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@krzysztof-tyb krzysztof-tyb force-pushed the cxx17_concepts_sparqlExpressions branch from 90b077e to 3c6751b Compare February 13, 2025 13:40
@krzysztof-tyb
Copy link
Contributor Author

@joka921 Thank you for your feedback!
I've added some modifications according to your comments.
Please let me know if you have more suggestions.

…essions' into cxx17_concepts_sparqlExpressions

# Conflicts:
#	src/backports/concepts.h
#	src/engine/Bind.cpp
#	src/engine/Filter.cpp
#	src/engine/GroupBy.cpp
#	src/engine/LazyGroupBy.cpp
#	src/engine/sparqlExpressions/AggregateExpression.h
#	src/engine/sparqlExpressions/ConditionalExpressions.cpp
#	src/engine/sparqlExpressions/SparqlExpressionGenerators.h
#	src/engine/sparqlExpressions/SparqlExpressionTypes.h
#	src/engine/sparqlExpressions/StringExpressions.cpp
# Conflicts:
#	src/backports/concepts.h
Signed-off-by: Johannes Kalmbach <[email protected]>
Copy link
Member

@joka921 joka921 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much.
I have merged this with the master, make sure to git pull before continuing.
We now have the following additional macro:
CPP_template_lambda(captures)(typename T)(T&& arg) (requires SingleExpressionResult<T>),
Please consistently use it for the places where it helps (I have marked them).

Also add the missing macro CPP_template_lambda_mut (the same but with mutable),
and use it in the places I marked accordingly.

For the macro implementation, please follow the style I now chose: The internal BLA_BLAJ_AUX_WHICH_ macros come into the internal CPPtemplate2.h file, and only the final exposition + the documentation is done in the user-facing concepts.h folder.

That should then be the final iteration for this PR.
Let me know if I can be of additional assistance.

constexpr static bool isVariable = std::is_same_v<T, ::Variable>;
constexpr static bool isStrongId = std::is_same_v<T, Id>;
auto visitor = CPP_lambda_mut(&)(auto&& singleResult)(
requires sparqlExpression::SingleExpressionResult<
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deacy_t is not needed, and maybe CPP_template_lambda_mut

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least get rid of all the decltype(singleResult) below.

Comment on lines 154 to 156
std::decay_t<decltype(singleResult)>>) {
if constexpr (std::is_same_v<std::decay_t<decltype(singleResult)>,
ad_utility::SetOfIntervals>) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPP_template_lambda

Comment on lines 211 to 214
auto visitor = CPP_lambda_mut(&)(auto&& singleResult)(
requires sparqlExpression::SingleExpressionResult<
std::decay_t<decltype(singleResult)>>) {
constexpr static bool isStrongId =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPP_template_lambda_mut

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • then replace all the decltype(...)s below.

&outCol]<sparqlExpression::SingleExpressionResult T>(
T&& singleResult) mutable {
auto visitor = CPP_lambda_mut(&evaluationContext, &resultTable, &localVocab,
&outCol)(auto&& singleResult)(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Comment on lines 68 to 72
auto visitConstantExpressionResult = CPP_lambda(
&nextUnboundIndices, &unboundIndices, &isUnbound, &result,
ctx)(auto&& childResult)(
requires SingleExpressionResult<std::decay_t<decltype(childResult)>> &&
isConstantResult<std::decay_t<decltype(childResult)>>) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPP_template_lambda

Comment on lines 97 to 101
CPP_lambda(&result, &unboundIndices, &nextUnboundIndices, &ctx,
&isUnbound)(auto&& childResult)(requires CPP_NOT(
isConstantResult<std::decay_t<decltype(childResult)>> &&
SingleExpressionResult<std::decay_t<decltype(childResult)>> &&
std::is_rvalue_reference_v<decltype(childResult)>)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPP_template_lambda

auto visitExpressionResult = CPP_lambda(
&visitConstantExpressionResult,
&visitVectorExpressionResult)(auto&& childResult)(
requires SingleExpressionResult<std::decay_t<decltype(childResult)>> &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPP_template_lambda

Comment on lines 109 to 113
CPP_lambda()(size_t numElements, EvaluationContext* context, auto&& input,
auto&& valueGetter)(
requires SingleExpressionResult<std::decay_t<decltype(input)>>) {
auto transformation = CPP_lambda(context, valueGetter)(auto&& i)(
requires ranges::invocable<std::decay_t<decltype(valueGetter)>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPP_template_lambda

requires std::is_rvalue_reference_v<T&&> {
if constexpr (isConstantResult<T>) {
auto visitSingleExpressionResult = CPP_lambda(&ctx, &result)(auto&& s)(
requires SingleExpressionResult<std::decay_t<decltype(s)>> &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPP_template_lambda

@joka921
Copy link
Member

joka921 commented Feb 14, 2025

I now see your Comment about []<typename T>(){} being technically C++20.
That is technically true, but we have discovered that your targeted toolchain (Gcc8.3/Qcc 8.3) supports them without warning in C++17 mode, so I am currently assuming that that is fine.
I would suggest the following: Use my suggestions to use this feature to make the code readable.
If at some point we find out that that is a problem because of compliance etc, then it will be my fault, and I will have to find a solution on my time, as I have guided you on that path.
Does that work for you?

@krzysztof-tyb
Copy link
Contributor Author

I used CPP_template_lambda macros as you suggested.
I think we can leave these template lambda features as they are now. In future, if they will cause a problem, we will think about it.
Thank you for your support with this PR!

@sparql-conformance
Copy link

Copy link
Member

@joka921 joka921 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much, this looks nice and clean now.

Copy link
Member

@joka921 joka921 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, that looks great now

@joka921 joka921 merged commit 2d6046a into ad-freiburg:master Feb 14, 2025
23 of 24 checks passed
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

Successfully merging this pull request may close these issues.

2 participants