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

[SUGGESTION] can we add a way to use attributes? #1252

Open
farmerpiki opened this issue Aug 21, 2024 · 20 comments
Open

[SUGGESTION] can we add a way to use attributes? #1252

farmerpiki opened this issue Aug 21, 2024 · 20 comments

Comments

@farmerpiki
Copy link
Contributor

I am not aware of any way to hint the branch predictor in the new syntax

I'd like to be able to use attributes, example cpp1 code:

if(condition) [[likely]] {
 ...
} else {
 ...
}
@hsutter
Copy link
Owner

hsutter commented Aug 21, 2024

Thanks for the suggestion! I haven't implemented attributes yet, but intend to.

@zaucy
Copy link

zaucy commented Aug 21, 2024

There might be some overlap with meta-functions and attributes. Maybe C++ attributes could be implemented as meta functions if they are allowed on if statements, expressions etc.

if condition @likely {
  // ...
} else {
  // ...
}

// or maybe it should be before the if
@likely if condition {
  // ...
} else {
  // ...
}

the meta-function would simply lower to

if(condition) [[likely]] {
 // ...
} else {
 // ...
}

@SebastianTroy
Copy link

SebastianTroy commented Aug 22, 2024 via email

@zaucy
Copy link

zaucy commented Aug 22, 2024

perhaps compiler_services could expose an "add/prepend cpp1" so the likely metafunction could look something like this:

likely: (s: cpp2::meta::statement) = {
  if s.is_if_statement() {
    s.append_cpp1("[[likely]]");
  } else {
    // TODO: add likely for other branches
    error("cannot add likely to non-if statement");
  }
}

@farmerpiki
Copy link
Contributor Author

farmerpiki commented Aug 22, 2024

perhaps compiler_services could expose an "add/prepend cpp1" so the likely metafunction could look something like this:

likely: (s: cpp2::meta::statement) = {
  if s.is_if_statement() {
    s.append_cpp1("[[likely]]");
  } else {
    // TODO: add likely for other branches
    error("cannot add likely to non-if statement");

uhm yes you can, you can add [[likely]] to switch statements(this just tells the compiler to test for those marked with likely first, it would probably work the same if you put them first in your code, but it might make it harder for a human to reason about, for example if 4 and 7 are the most likely cases for an int... and you put them first it might seem to people reading the code that some cases were skipped). In many cases you should add [[unlikely]] to the default switch case

@farmerpiki
Copy link
Contributor Author

we actually should have both likely and unlikely ... likely means prioritize this case and unlikely means put this code in another bucket because it might never get called and it allows for keeping the happy flow closer together in cache so that when all goes well it goes faster

@farmerpiki
Copy link
Contributor Author

this suggestion was meant to be more generic though... I want to be able to add attributes to functions as well... and a way to add them so that it just works no matter the compiler or attribute I want to be able to use

[[gnu::target_clones("default", "sse4.2", "avx", "avx2")]]

and others as well

@dutkalex
Copy link
Contributor

Using metafunctions to add attributes makes a lot of sense IMO:

  • it fits nicely in the "reducing the concept count" and "simplification through generalization" principles
  • attributes can become a library feature rather than a core language thing, which enables the use of non-standard (user-defined or compiler-dependant) ones for special purposes

@DyXel
Copy link
Contributor

DyXel commented Aug 22, 2024

When all you have is a hammer, everything looks like a nail. I personally don't believe metafunctions / metaprograms are the right approach for attributes, as they are supposed to be hints for the implementation, rather than having big impact on the program you are writing (at least that's my perception).

@dutkalex
Copy link
Contributor

I agree that it makes sense to distinguish things that are just optimization hints which can be ignored from metafunctions which offer guarantees.
However, coming from an HPC background, I have observed that:

  • people tend to rely on certain optimizations actually being implemented (I know that this is not really a good argument to guide language design but non-experts tend to think that if "it's written then it's guaranteed" and I think it is worth taking that into account)
  • in order to leverage compiler-specific attributes and attribute-like language extensions (__device__ annotations for gpu programming for example) in a portable way, the answer is almost always to use macros because there is no other way to insert logic before stamping a function with an attribute. Cpp2 offers a natural alternative with metafunctions, which can encapsulate the raw attributes into solid domain specific abstractions. I believe that in this context most code will never use raw attributes and instead rely on more expressive metafunctions, hence the idea that maybe attributes should simply be primitive metafunctions.

@DyXel
Copy link
Contributor

DyXel commented Aug 22, 2024

As always, the right answer is probably somewhere in the middle. If you are relying on a certain optimization (e.g. loop unrolling), it might be a better idea to write a little reflection tool that does that for you automatically so you get a stronger guarantee, and attributes remain for hinting the implementation. In this case I would say we need both and that they are orthogonal. I am curious what Herb thinks about completely subsuming attributes as part of the metafunction syntax, its an interesting take for sure.

@dutkalex
Copy link
Contributor

dutkalex commented Aug 22, 2024

The line between what is just an optimization hint and what should have first class support can be blurry. For example, one can argue that loop unrolling is just an optimization and that this should be left to the compiler (maybe with the ability to add an attribute as a hint).
But this currently leads to loads of TMP hacks to achieve the same purpose with guarantees. I personally have in my codebase something along these lines for this exact purpose, because it extends the expressive power of the language:

static_for< 0, N >( [&]( auto i ){
  some_regular_function_call( i.value );
  some_template_function_call< i.value >();
} );

EDIT: it seems like you have mind-reading abilities @DyXel 😅

@SebastianTroy
Copy link

SebastianTroy commented Aug 22, 2024 via email

@dutkalex
Copy link
Contributor

dutkalex commented Aug 22, 2024

I don't see how a library can implement some of these features

I don't see why we must have an attribute syntax in cpp2 to emit cpp1 code with attributes. Am I missing something there?

@SebastianTroy
Copy link

SebastianTroy commented Aug 22, 2024 via email

@zaucy
Copy link

zaucy commented Aug 22, 2024

not to detract from C++ attributes too much here, but to expand the idea of "emitting cpp1" in a metafunction (which would cover C++ attributes) - the unreal engine has a sort of "attribute" that they decorate their classes and functions with that I think having an "emitting cpp1" feature could cover.

for instance you could write an unreal engine class in cpp2 like this:

AMyActor: @uclass type = {
  this: AActor;
  // ...
};

that would lower to this

UCLASS()
class AMyActor : public AActor {
  GENERATED_BODY()
};

@DyXel
Copy link
Contributor

DyXel commented Aug 22, 2024

Ideally, the metaclass would expand to whatever the macro expands to, not to the macro itself. Same idea with Q_OBJECT in Qt. Herb touched on this in one of his talks, I don't remember exactly which one though 😅

@zaucy
Copy link

zaucy commented Aug 22, 2024

Ideally, the metaclass would expand to whatever the macro expands to, not to the macro itself

Totally agree, but it would be a really nice stepping stone. Especially for the unreal example above since if it could emit what I described it could be used today since it could use existing tools (unreal header tool, unreal build tool, etc.)

@JohelEGP
Copy link
Contributor

JohelEGP commented Oct 6, 2024

I've been waiting on [[no_unique_address]].

I also use this to have [[no_unique_address]] members:

#define QTY_NAME_MEM_TYPE [[no_unique_address]] name_type

-- #658 (comment)

@JohelEGP
Copy link
Contributor

I agree that we shouldn't abuse metafunctions.

The likes of @java_interface and @qt::moc are not only fine,
they are one of the intended audience of metafunctions.
Even if they don't modify the type, but only read it to write some output.

There are other features we don't have in Cpp2 yet.
These aren't Cpp1 attributes, but could be expressed in their Cpp2 syntax or as metafunctions.

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

No branches or pull requests

7 participants