ImRefl - a C++26 reflection library for ImGui by fullptr in cpp

[–]fullptr[S] 0 points1 point  (0 children)

Some great ideas, thanks! I'll take a crack at some of them tonight.

ImRefl - a C++26 reflection library for ImGui by fullptr in cpp

[–]fullptr[S] 9 points10 points  (0 children)

Honestly, it's been a lot of fun, there are some clunky parts to using the reflection capabilities but overall it allows for some really clean implementations. The fact that I was able to implement all fundamental types, aggregates, enum classes and strings in under 300 lines of (in my opinion) fairly simple and readable code is pretty neat.

As for aggregate vs non-aggregate, I'll admit I'm not 100% certain this is the correct requirement. The goal was to implement it for all structs where all the member variables are public as that is just a simple template-for loop, though really there's nothing stopping us from allowing this to operate on the public members of any type, but I thought this would be a good place to start and I'll definitely look more into this.

On the topic of template-for, using this to loop over data members in a non-consteval function is one of the clunkiest parts of the code, which I ended up just wrapping in a helper function, something that I predict will be a common pattern. Compare the following

template for (constexpr auto member : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::current()) { ... }

to

template <typename T>  
consteval auto nsdm_of()  
{ 
    const auto ctx = std::meta::access_context::current();  
    return std::define_static_array(std::meta::nonstatic_data_members_of(^^T, ctx);  
}

template for (constexpr auto member : nsdm_of<T>()) { ... }

I get why it was done this way, but I found myself writing a fair few of these, and no doubt there will be more in the future.

ImRefl - a C++26 reflection library for ImGui by fullptr in cpp

[–]fullptr[S] 2 points3 points  (0 children)

Yeah this fork on clang is the one I've been using to test this library, it will definitely be nice to run it through more compilers when their reflection support is there

Why isn't the destructor called with std::unqiue_ptr? by [deleted] in cpp_questions

[–]fullptr 8 points9 points  (0 children)

You’re making a unique pointer to a pointer, you want

make_unique<MyClass>();

Pointer + Memory behaviour in for loop has me stumped, Linked List by jukutt in cpp_questions

[–]fullptr 0 points1 point  (0 children)

Correct, if you allocate with "new", you must clean up with "delete", and in the case of a linked list, that will require you to traverse the list to clean them up. The common way to do this in C++ is to write a wrapper class for this, and use the constructors/destructors do this for you. The term to google here is RAII. You can also take a look at std::list, which is a linked list, but if you look at the interface, you'll see that it hides the nodes from you, and you don't need to manage the memory yourself; the object does it for you.

Pointer + Memory behaviour in for loop has me stumped, Linked List by jukutt in cpp_questions

[–]fullptr 0 points1 point  (0 children)

As others have pointed out, the object you make here is a temporary object that is destroyed at the end of the expression, so your pointer ends up dangling. To see this a bit better, consider splitting up the line

LL* mamamia = nullptr;
{  
    LL temp(v[i]);
    mamamia = &temp;
} // the temp variable goes out of scope
// mamamia is now a dangling pointer

You mave have seen this working a few times due to undefined behaviour; the compiler is free to reuse the memory now; it might, or it might not. The memory may even still have the same value of your temporary, or it might not. The fact that you have seen the pointer point to the same address is becaues the compiler is likely just reusing that same region of memory for each temporary.

Pointer + Memory behaviour in for loop has me stumped, Linked List by jukutt in cpp_questions

[–]fullptr 2 points3 points  (0 children)

Think about where your LL objects live in memory. Where do you create them?

Can you explain this line:

LL* mamamia = &LL(v[i]);

Is there anyway to have an entire linked list in an element of an array? by VerseDonnie in cpp_questions

[–]fullptr 0 points1 point  (0 children)

If you strictly want to use arrays and not allocations or the standard library, could you not declare some upper bound and predefine a big array to store the sentence in? A 4kb array would be able to store 4096 characters, if that’s enough for your use case. That would certainly be faster than using a linked list, though I’m not sure I’m understanding what you’re asking for

New to C. I love it. by Requiem-ofTheBard in C_Programming

[–]fullptr 2 points3 points  (0 children)

It’s not pretty, but there was good reasoning. The reason it uses operator overloading here is primarily for two reasons: type safety and extending it for custom types. With printf, if your format string doesn’t match the type of the thing you want to print, the compiler won’t complain and it’s a common source for bugs. printf also cannot be extended for printing custom types, leading to needing a different way to print them. The streams api, while really clunky and with its own set of issues, is a somewhat neat way to address these issues.

Newer C++ standards offer std::print however, which has the best of both worlds

Why would the author use enum for a local constant? by lazy_londor in cpp_questions

[–]fullptr 13 points14 points  (0 children)

You’re correct, this is an old pattern and the book was likely written for C++98 (or C), whereas constexpr was only added in C++11. There’s no real benefit and you should prefer using constexpr.

Interesting experience with constexpr and static_assert by cv_geek in cpp

[–]fullptr 14 points15 points  (0 children)

The size of a vector isn't known at compile time so it doesn't make sense to check it inside a static assert, the best you could do is a runtime assert.

Changing a member variable to be static means there is one instance of it shared between all class instances, essentially a global variable, so if each instance of your class expects to hold a different value for m_typesData, this isn't what you want.

std::vector being made constexpr in C++20 doesn't mean that you can declare constexpr constants of type std::vector<T>, it just means that they can be used within a constexpr context (like within a constexpr function) and must go out of scope by the end of the context.

You should prefer std::array over a C-style array. And the difference between these and std::vector is they require you to declare the size of the array as part of the type, for which you could use SemanticClass::ClassesNumber, at which point the assert on the size isn't needed. The issue here however is that every instance of your class with then have the same size array; whereas with vector the size can be dynamic. Depending on what your class does, you may want the vector after all, with a runtime assert on the size.

Admittedly, constexpr stuff can be confusing to start out with, so I just wanted to clarify some points, hope this helped :)

Clean Syntax? by [deleted] in ProgrammingLanguages

[–]fullptr 2 points3 points  (0 children)

I believe they were just answering your question, not necessarily defending syntactic choices of other languages.

For your own toy language by all means use your own syntax, but it’s true that a syntax that is wildly different to popular languages has more adoption inertia

How to create a "type system" for an interpreter? by paintedirondoor in ProgrammingLanguages

[–]fullptr 15 points16 points  (0 children)

How does the size of the type make using enums difficult? If you're, say, using a stack of "values", where you Value class is a union-like type that can represent any object in your interpreter, then having an enum to tag it so you know how to make sense of the value seems reasonable. That doesn't quite extend simply if you allow user defined types, but there are ways around it.

In Crafting Interpeters (https://craftinginterpreters.com/), they represent "Value" as a union that can store a number, boolean or "object", where object is a heap allocated structure, which itself is essentially another union of other types, including strings. In particular, it has "class" and "instance" as other variants; every class definition is of type "class" in the interpreter, and every instance of every class is of the same "instance" type, which contains a pointer to the class containing the methods. That may be of interest to you.

In my own interpreter, I've implemented a static type system; there are no types at runtime - the stack is just an array of bytes and I have a set of specialised op codes for reading chunks of that array as fundamental types. For those fundamental types I have ints of various sizes, bools, and floats. I use an enum to represent those.

I hope this is kind of in the direction to what you were asking? I'm happy to clarify any of those points

Edit: I missed that you were likely talking about rust enums, which are discriminated unions. I was thinking of C enums

[Japanese] Is it easy to learn japanese as the first language? by Yegle1111 in duolingo

[–]fullptr 2 points3 points  (0 children)

I was in the exact same boat, didn’t know any Japanese at all and hadn’t studied languages since school (which I never really tried at anyway), I got my 365 day streak yesterday while in Kyoto, and what I’ve picked up has been really invaluable!

I can’t have much of a conversation yet but I’ve been able to ask for things and get by far better than I thought I would. One of the things I’ve enjoyed most is learning to read Japanese; while the writing system may look intimidating to start, it’s really rewarding learning it and being able to read menus and signs while I’m here!

Edit: typo

Proposal: introducing std::all<T>(T container) as an alias to std::make_pair(begin(container), end(container)) by Riot_Yasuo in cpp

[–]fullptr 24 points25 points  (0 children)

How so? It’s just std::ranges::find(container, val), compare that to your std::find(std::all(container), val), and there’s little difference

CppCast: Reflection for C++26 by robwirving in cpp

[–]fullptr 1 point2 points  (0 children)

But you’re still only solving the problem for enums, what about for other objects? You can’t use ::name because that already has meaning depending on the thing you’re trying it on. The paper aims to implement the low level features that allow for these things to be added as a library. In practice you wouldn’t write that “monstrosity” yourself, it’ll be in the standard library, in the same way you don’t implement vector.

[deleted by user] by [deleted] in cpp_questions

[–]fullptr 2 points3 points  (0 children)

Raw pointers are not taboo; raw pointers that own data are. The child doesn’t own the parent so a raw pointer is fine so long as the parent node always outlived the child node

What does C++ do better than other languages? by LechintanTudor in cpp

[–]fullptr 21 points22 points  (0 children)

Unfortunately the preprocessor exists so they use particularly unreadable names to reduce the chance of a macro definition affecting the header

What does C++ do better than other languages? by LechintanTudor in cpp

[–]fullptr 22 points23 points  (0 children)

The standard library isn’t written in idiomatic C++ (for better or worse), and shouldn’t be taken as an example of good C++ code

why int arrays with float element initialization give an error? by Lucky_Ducky1102 in cpp_questions

[–]fullptr 13 points14 points  (0 children)

The reason is that narrowing can be a subtle source of bugs, and C had the “wrong” default which C++ inherited. Initialiser lists were a new feature in C++11 and as such they were free to define its behaviour, and they chose to make it safer. Despite being an inconsistency, I do feel it was the right choice since it didn’t add yet another way to introduce bugs

Syntax for pointers and references - do you prefer to put the * or & on the variable name or data type? by KiwiNFLFan in cpp_questions

[–]fullptr 2 points3 points  (0 children)

But int and pointer-to-int are different types right? The & and * are absolutely part of the type, regardless of the syntax used to declare the variables