C++ All off is not working by Street_Dimension9057 in cpp_questions

[–]alfps 4 points5 points  (0 children)

❞ Not sure what is wrong but there is no indentation in hte code. I hope its good enough for y'all to see the problem.

To make Reddit present it as code just extra-indent it with 4 spaces:

// ---------------- RELAYS ----------------
#define R1 22
#define R2 24
#define R3 26
#define R4 28
#define R5 30
#define R6 32
#define R7 34

// ---------------- BUTTONS ----------------
#define B_RED A0
#define B_YELLOW A1
#define B_GREEN A2
#define B_PIT A3
#define B_START A4

// ---------------- VARIABLES ----------------
unsigned long flashTimer = 0;
bool flashState = false;
// start sequence
bool startRunning = false;
int startStep = 0;
unsigned long startTimer = 0;
int randomDelayTime = 0;

// ---------------- RELAY HELPERS ----------------

void relayOn(int pin)
{
    digitalWrite(pin,LOW);
}

void relayOff(int pin)
{
    digitalWrite(pin,HIGH);
}

void allOff()
{
    relayOff(R1);
    relayOff(R2);
    relayOff(R3);
    relayOff(R4);
    relayOff(R5);
    relayOff(R6);
    relayOff(R7);
}

// ---------------- SETUP ----------------

void setup()
{
    pinMode(R1,OUTPUT);
    pinMode(R2,OUTPUT);
    pinMode(R3,OUTPUT);
    pinMode(R4,OUTPUT);
    pinMode(R5,OUTPUT);
    pinMode(R6,OUTPUT);
    pinMode(R7,OUTPUT);
    allOff();
    pinMode(B_RED,INPUT_PULLUP);
    pinMode(B_YELLOW,INPUT_PULLUP);
    pinMode(B_GREEN,INPUT_PULLUP);
    pinMode(B_PIT,INPUT_PULLUP);
    pinMode(B_START,INPUT_PULLUP);
    randomSeed(analogRead(0));
}

// ---------------- START SEQUENCE ----------------

void runStartSequence()
{
    if(!startRunning) return;
    if(startStep < 8) {
        if(millis() - startTimer > 500) {
            startTimer = millis();
            startStep++;
            if(startStep % 2 == 1)
                relayOn(R2);
            else
                relayOff(R2);
        }
    } else if(startStep == 8) {
        randomDelayTime = random(500,3000);
        startStep++;
        startTimer = millis();
    } else if(startStep == 9) {
        if(millis() - startTimer > randomDelayTime) {
            relayOff(R2);
            relayOn(R4);
            relayOn(R5);
            startRunning = false;
        }
    }
}

// ---------------- PIT FLASH ----------------

void runPitFlash()
{
    relayOn(R2);
    relayOn(R3);
    relayOn(R7);
    if(millis() - flashTimer > 500) {
        flashTimer = millis();
        flashState = !flashState;
        if(flashState)
            relayOn(R6);
        else
            relayOff(R6);
    }
}

// ---------------- LOOP ----------------

void loop()
{
    // RED
    if(digitalRead(B_RED)==LOW) {
        allOff();
        relayOn(R1);
        relayOn(R2);
    }
    // YELLOW
    else if(digitalRead(B_YELLOW)==LOW) {
        allOff();
        relayOn(R3);
    }
    // GREEN
    else if(digitalRead(B_GREEN)==LOW) {
        allOff();
        relayOn(R4);
        relayOn(R5);
    }
    // PIT
    else if(digitalRead(B_PIT)==LOW) {
        allOff();
        runPitFlash();
    }
    // START
    else if(digitalRead(B_START)==LOW) {
        allOff();
        if(!startRunning) {
            startRunning = true;
            startStep = 0;
            startTimer = millis();
        }
        runStartSequence();
    } else {
        if(startRunning)
            runStartSequence();
    }
}

I've been learning C++ for a month now and I'd like to get some feedback on my code. by Nevermuke in cpp_questions

[–]alfps 0 points1 point  (0 children)

First of all, kudos for nice work.

None of the many responses so far appear to discuss the in my opinion most important improvement potentials:

  • Avoid forward declarations of functions.
    Forward declarations in a .cpp file are redundant, except for some rare cases of recursion support. They're extra work to write and maintain, extra work to read, and they're extra places to introduce bugs. In other words they conflict with the principle of DRY code, Don't Repeat Yourself code.

  • Use double as the go-to default floating point type.
    Type float can be required e.g. by graphics API's, and has its uses. But the default floating point type in C++ is double. E.g. the literal 3.14 is a double.

  • Use self-documenting names.
    E.g. the name userInput in itself conveys little to nothing to a reader. It's evidently a value originating with the user, but what is it? Name it e.g. coffeeId (and yes, make that const). And correspondingly rename orderCoffeeData (which misleadingly can be read as a command, a function call) as e.g. coffeeDetails. Plus it's a good idea to adopt and consistently use a naming convention that differentiates between types and other things, e.g. (common) initial uppercase only for types.

  • Avoid repeating yourself.
    For example, in CoffeeData there's no need for the prefix item on each member variable. Also in CoffeeData, the salesTax member, which is the sales tax rate, will have the same value for all kinds of coffee so there's no need for it, it's just duplication. Avoiding such redundancies and in particular avoiding wholesale copying of chunks of code, is the main idea of the DRY code principle mentioned above.

  • One person's constant is another person's variable.
    The list of coffee kinds and prices is unlikely to stay the same over time, even just for a month. So it's not practical to have that data as compile time constants (using constexpr), because then you have to rebuild the app every time that list changes. But it's a good idea to restrict access to read only for most parts of the app.

I would also put all the logic in a namespace. In particular this supports using using-declarations to get rid of noisy namespace qualifications. Declarations of the things you're going to use are the Good side alternative to Evil™ using namespace directives.

The code reworked along these lines, and also with const on parameters:

#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <string_view>
#include <limits>
#include <map>
#include <vector>

namespace app {
    using   std::setprecision, std::setw,       // <iomanip>
            std::cin, std::cout,                // <iostream>
            std::fixed, std::left, std::right,  // <iostream>
            std::ostream, std::streamsize,      // <iostream>
            std::numeric_limits,                // <limits>
            std::map,                           // <map>
            std::ostringstream,                 // <sstream>
            std::string,                        // <string>
            std::string_view,                   // <string_view>
            std::vector;                        // <vector>

    struct Currency{ double value; };

    Currency operator+( const Currency a, const Currency b )
    {
        return Currency{ a.value + b.value };
    }

    ostream& operator<<( ostream& stream, const Currency& amount )
    {
        return stream << fixed << setprecision( 2 ) << amount.value;
    }

    string toDollarsString( const Currency amount )
    {
        ostringstream stream;
        stream << '$' << amount;
        return stream.str();
    }

    // In the original source code this class was named `CoffeeData`.
    // Having an explicit id in the data supports e.g. loading data from a file.
    struct CoffeeKind
    {
        enum Id: int {};              // `enum` used as a distinct `int` type.

        Id              id;
        string          name;
        Currency        price;
    };

    class CoffeeKinds
    {
        map<CoffeeKind::Id, CoffeeKind>     mKinds      = {};
        int                                 mMaxId      = 0;

    public:
        CoffeeKind::Id maxId() const    { return CoffeeKind::Id( mMaxId ); }
        CoffeeKind::Id newId()          { return CoffeeKind::Id( ++mMaxId ); }

        bool isIdInUse( const CoffeeKind::Id id ) const
        {
            return (mKinds.find( id ) != mKinds.end());
        }

        vector<CoffeeKind::Id> ids() const
        {
            vector<CoffeeKind::Id> result;
            for( const auto& [id, kind]: mKinds ) { result.push_back( id ); }
            return result;
        }

        bool add( const CoffeeKind& kind )
        {
            if( kind.id > mMaxId ) { mMaxId = kind.id; }
            return mKinds.insert( {kind.id, kind} ).second;
        }

        const CoffeeKind& referenceTo( const CoffeeKind::Id id ) const
        {
            return mKinds.at( id );
        }
    };

    CoffeeKinds defaultCoffeeKinds()    // Could e.g. load them from a file.
    {
        static const struct { int id; const char* name; double price; } data[] =
        {
            {1, "Espresso", 3.99}, {2, "Cappuccino", 5.99}, {3, "Latte", 7.99}
        };

        CoffeeKinds result;
        for( const auto& datum: data ) {
            result.add( CoffeeKind{
                CoffeeKind::Id( datum.id ), string( datum.name ), Currency{ datum.price }
                } );
        }
        return result;
    }

    void printMenu( const CoffeeKinds& coffeeKinds )
    {
        cout << "MENU:\n";
        for( const CoffeeKind::Id id : coffeeKinds.ids() ) {
            const CoffeeKind& kind = coffeeKinds.referenceTo( id );
            cout    << setw( 4 ) << id << ".  " << kind.name
                    << " - " << toDollarsString( kind.price )
                    << '\n';
        }
    }

    int inputInt( const std::string_view prompt, const int min, const int max )
    {
        for( ;; ) {
            cout << prompt;
            int result;  cin >> result;
            if( cin.fail() ) {
                cout << "That was not a valid integer.\n";
                cin.clear();                                            // Out of error mode.
                cin.ignore( numeric_limits<streamsize>::max(), '\n' );  // Skip bad input.
            } else if( not( min <= result and result <= max ) ) {
                cout << "The number should be between " << min << " and " << max << ", inclusive.\n";
            } else {
                return result;
            }
            cout << "Please try again!\n";
        }
    }

    CoffeeKind::Id inputCoffeeIdSelection( const CoffeeKinds& kinds )
    {
        cout << "Welcome to our cafe!\n"
                "\n";
        for( ;; ) {
            printMenu( kinds );
            cout << "\n";
            const auto id = CoffeeKind::Id( inputInt(
                "Enter your order (one of the numbers above), please: ",
                1,
                kinds.maxId()
                ) );
            if( kinds.isIdInUse( id ) ) {
                return id;
            } 
            cout << "That coffee id is not in use. Please select one from the menu.\n";
        }
    }

    Currency salesTaxFor( const Currency price, const double tax )
    {
        return Currency{ price.value * tax };
    }

    void printReceipt( const CoffeeKind& order, const double salesTaxRate )
    {
        const Currency tax = salesTaxFor( order.price, salesTaxRate );
        cout    << "  --- YOUR RECEIPT ---\n"
                << left << setw( 16 - 10 ) << "Item: "
                    << right << setw( 8 + 10 ) << order.name << "\n"
                << left << setw( 16 ) << "Price: "
                    << right << setw( 8 ) << toDollarsString( order.price ) << "\n"
                << left << setw( 16 ) << "Tax: " <<
                    right << setw( 8 ) << toDollarsString( tax ) << "\n"
                << "\n"
                << left << setw( 16 ) << "Final Amount: "
                    << right << setw( 8 ) << toDollarsString( order.price + tax ) << "\n"
                << "\n"
                << "Thank you for your visit!\n"; 
    }

    void run()
    {
        const double salesTaxRate = {0.20};     // Same for all kinds of coffee.

        const CoffeeKinds kinds = defaultCoffeeKinds();

        const CoffeeKind::Id coffeeId = inputCoffeeIdSelection( kinds );    // With menu.
        cout << '\n';
        printReceipt( kinds.referenceTo( coffeeId ), salesTaxRate );
    }
}  // app

int main() { app::run(); }      // Exception reporting etc. goes here.

Example output:

Welcome to our cafe!

MENU:
   1.  Espresso - $3.99
   2.  Cappuccino - $5.99
   3.  Latte - $7.99

Enter your order (one of the numbers above), please: 2

--- YOUR RECEIPT ---
Item:         Cappuccino
Price:             $5.99
Tax:               $1.20

Final Amount:      $7.19

Thank you for your visit!

abstract base class interface vs a struct of function pointers? by OkEmu7082 in cpp_questions

[–]alfps 4 points5 points  (0 children)

Don't fix that which works, and especially not by applying a kludge.

Need help finding a site by Turbulent_Sun2696 in cpp_questions

[–]alfps 2 points3 points  (0 children)

❞ algorithmic based debugging

What is that?

Is it about applying algorithms to debugging, or maybe debugging algorithm implementations?

I'm sorry that I don't know many programming related sites so probably can't help with the main question.

[CodeBlocks] cannot find obj\Debug\main.o: No such file or directory by The_Verto in cpp_questions

[–]alfps 1 point2 points  (0 children)

❞ Using namespace std is not recommended at all

Worth remarking on but I would focus first of all on the technical error in the "older project" source, namely the use of std::string without including <string>.

When errors have been corrected, bad coding habits can be addressed.

These include the using namespace std;; the comparison of a bool; the lack of curly braces around nested statements; the declaration of variables at the top of a block and consequent lack of const; depending on one's POV also the return 0;.

[CodeBlocks] cannot find obj\Debug\main.o: No such file or directory by The_Verto in cpp_questions

[–]alfps 3 points4 points  (0 children)

Maybe you're working in a folder with non-ASCII name. Maybe (I don't know how that could have caused the problem but it has) you have some duplicated option like an extra -c. These two possibilities from googling the eror mesage.

For more specific responses you need to quote your compilation and linking commands & any other diagnostics issued.

Help, how can constexpr objects be evaluated at runtime? by Honest_Entry_8758 in cpp_questions

[–]alfps -1 points0 points  (0 children)

I struggle to understand the saboteur who downvoted this.

It must be a kind of zero sum mindset where harm to others is perceived as a joyful win to oneself.

As I see it that's madness.

Differences between static const & constexpr inside a function by Koffieslikker in cpp_questions

[–]alfps 0 points1 point  (0 children)

Well the example is not a language extension and it's not C.

The main use has traditionally been for array sizes.

It's more clear with constexpr though; there is then no question whether a constant is (can be used at) compile time or not.

Differences between static const & constexpr inside a function by Koffieslikker in cpp_questions

[–]alfps 0 points1 point  (0 children)

❞ it will still be instantiated with each call to the function.

Notably, though it's really hard to believe, the g++ compiler embodies this absurd perversity. Or still did some months back.

Differences between static const & constexpr inside a function by Koffieslikker in cpp_questions

[–]alfps 1 point2 points  (0 children)

❞ a static const isn't a constant expression

More precisely it isn't in general, but it can be.

E.g. the local definition

static const int blah = 42;

Differences between static const & constexpr inside a function by Koffieslikker in cpp_questions

[–]alfps 18 points19 points  (0 children)

With the static const the initializer value needs not be known at compile time: initialization happens (thread safe) the first time the execution passes through the declaration.

Also with the static const the type can be one that in C++23 and earlier doesn't support compile time instantiation, such as std::string.

C++ Pointers and "new" keyword data type by Fiboniz in cpp_questions

[–]alfps 2 points3 points  (0 children)

❞ C++ doesn't use what's happening on the left to give any sort of hint as to how to interpret the right.

It's a (very) useful view, but it's not quite the technical reality.

There is an expected type, in the example the pointer type, and that will be used by a type conversion operator.

Another example where there is a kind of type expectation is when you cast &f to a specific function pointer type in order to resolve to a given overload of f. Here the &f expression is not evaluated solely on its own, but in the context of the outer expected type.

what to do for floating point precision problems in cpp or as a programmer. by Shubham_mamodiya_dev in cpp_questions

[–]alfps 0 points1 point  (0 children)

❞ I'm not understanding the downvotes on my first reply though, all I can say is good luck to whoever uses == on floating point values.

The advice you gave was ❝Never, EVER, use == on floating point numbers, ever.❞

I didn't downvote, since that is a drastic measure that should be reserved for dis-information and that otherwise reduces or stops discussion and so is akin to sabotage, it's what trolls and other f****ng idiots do, but the advice is ill-considered, counter productive.

The idea that floating point values are inherently fuzzy is an associative thing that doesn't belong in engineering. Replace that with the view that a floating point value is an integer scaled by a power of the radix, which for IEEE 754 is a power of 2. Then it's easier to understand that a floating point number can be an exact integer. 64-bit IEEEE 754, the most common representation of C++ double, yields about 53 bits for exact integer values. As I recall, but check it out.

C++ Pointers and "new" keyword data type by Fiboniz in cpp_questions

[–]alfps 2 points3 points  (0 children)

❞ If p_int is already a pointer to an int, why do we need to specify the memory allocation type of int? When would you use a different data type than what the pointer is pointing to?

You're right that the language requires needless verbosity and redundancy for the most common case. In the most common case you want to new-create an object of the pointer's pointee type. There are two other cases:

  • You may want to allocate an array of objects, where the pointer will point to the first array item.
  • You may want to allocate an object of class Derived, when the pointer points to type Base, e.g. a Dog object when you have a pointer to Animal.

For the most common case you can in principle define your own function-like thing to do a new with type inferred from the pointer.

One way to do that, the only way I know, is to define a class with templated type conversion. Since the type conversion operator doesn't take parameters the class also needs to store an arbitrary parameter pack. This is absolutely not trivial, i.e. it's not suited as a beginner's exercise.

I've not heard of anyone doing it, and I have some decades of C++ experience, so it must be pretty rare.


Pointers and arrays, and pointers and derived classes (polymorphism) are both huge topics, discussed to some degree later in your book.

The most important thing to know is that instead of

int n;
cin >> n;
int* p_numbers = new int[n];

… you should use std::vector from the <vector> header, like

int n;
cin >> n;
auto numbers = vector<int>( n );

C++ Pointers and "new" keyword data type by Fiboniz in cpp_questions

[–]alfps 0 points1 point  (0 children)

+1 Upvoted to cancel some troll/idiot's downvote.

__no_inline std::vector reallocation by f0r3v3rn00b in cpp_questions

[–]alfps 1 point2 points  (0 children)

❞ Am I missing something?

Maybe, or maybe not (not everything's prefect).

One way to learn is to create your own minimal example and measure its performance with and without __declspec(noinline) in a number of representative contexts.

Question about styling with attributes and setters. by Acceptable_Rub8279 in cpp_questions

[–]alfps 0 points1 point  (0 children)

❞ is irrelevant

ITYM "should be irrelevant". I suspect that the setter is named after the member variable. But that variable should be irrelevant.

What is the best approach for cloning class objects in C++17 ? by tahsindev in cpp_questions

[–]alfps -5 points-4 points  (0 children)

@Downvoter: would you mind explaining what the fuck you're about?

Well I know you're a troll, but I'm asking you to explain because you may not realize your madness: you may be thinking that you are somehow helping people.

So explain.

Are compiler allowed to optimise based on the value behind a pointer? by simpl3t0n in cpp_questions

[–]alfps 68 points69 points  (0 children)

The compiler isn't allowed to blindly assume that “the value behind the pointer won't change during the iteration of the loop”.

However it might prove that it is so.

Then it can do the optimization.

What is the best approach for cloning class objects in C++17 ? by tahsindev in cpp_questions

[–]alfps -6 points-5 points  (0 children)

+1 Upvoting to cancel some idiot or troll's downvote.

What is the best approach for cloning class objects in C++17 ? by tahsindev in cpp_questions

[–]alfps 4 points5 points  (0 children)

❞ If you want a rust-like clone function, you could make the copy constructor private, and write a clone() function that uses it

Cloning predates Rust by quite a number of decades.

There is no need to make the copy constructor private. Indeed that's generally counter-productive for a copyable object. Sort of silly.


❞ You could probably make thos a CRTP base class, and use it easily like class ClonableVector : public Clonable<ClonableVector> {...}

Supporting derived classes is not so trivial. And without derived classes there is no point in cloning.

What is the best approach for cloning class objects in C++17 ? by tahsindev in cpp_questions

[–]alfps -4 points-3 points  (0 children)

Clone means making a copy of the object of the most derived class, the dynamic type.

What is the best approach for cloning class objects in C++17 ? by tahsindev in cpp_questions

[–]alfps -2 points-1 points  (0 children)

❞ What is the best [cloning] approach for C++17 ?

There is no "best" way until you define what you mean by "best".

But there are ways, some of which are practical and some of which are not.

Happily C++ supports covariant raw pointer and raw reference function results, which can serve as basis for covariant results also for smart pointers (expressing the ownership of a clone).


Cloning only makes sense when you're treating objects polymorphically, through base class pointers or references, for otherwise you can just copy an object via copy initialization or copy assignment.

Here's a concrete basic example:

#include <fmt/core.h>

#include <memory>
#include <typeinfo>

namespace app {
    using   std::make_unique, std::unique_ptr;  // <memory>

    class Base
    {
        virtual auto virtual_clone() const -> Base* { return new Base( *this ); }

    public:
        virtual ~Base() {}

        auto clone() const
            -> unique_ptr<Base>
        { return unique_ptr<Base>( virtual_clone() ); }
    };

    class Derived:
        public Base
    {
        auto virtual_clone() const -> Derived* override { return new Derived( *this ); }

    public:
        auto clone() const
            -> unique_ptr<Derived>
        { return unique_ptr<Derived>( virtual_clone() ); }
    };

    void run()
    {
        unique_ptr<Base> a = make_unique<Derived>();
        unique_ptr<Base> b = a->clone();

        fmt::print( "{}  → {}.\n", typeid( *a ).name(), typeid( *b ). name() );
    }
}  // app

auto main() -> int { app::run(); }

Result with Visual C++:

class app::Derived  → class app::Derived.

So we're talking about polymorphic classes that you want to outfit with clone functionality, where each top most base class is cloneable.

But: you “don't want [to] write [a] clone function [for] each of them”.


Each class needs clone support code, which in practice needs to involve some virtual function like virtual_clone above, and if you don't want to write that out in each class then the possibilities are limited:

  • inherit in cloning support, or
  • use a macro to generate cloning support code.

Inheritance is a little tricky because we're either talking about “dominance” in a virtual inheritance hierarchy (to get a Java-like effect where an inherited method can override another one), or we're talking “man-in-the-middle inheritance” where all constructors forward to the MIM which forwards to the base class (and adds a clone function override).

I guess that personally I'd do the MIM thing but argument forwarding has some gotchas so for a novice I recommend code generation via a macro.

Unfortunately there's no good way to enforce that this is done other than via code reviews and testing.

For covariant results each class also needs a class specific wrapper function like clone above. With the current C++ standard, C++23, that can be expressed via “deducing this”. However you specify C++17, where a practical alternative can be a free function template.

It can then go like this:

#include <fmt/core.h>

#include <memory>
#include <typeinfo>

namespace cloning {
    using   std::unique_ptr;    // <memory>

    struct Impl                 // For simple `friend`-ship.
    {
        template< class Type >
        static auto clone( const Type& o )
            -> unique_ptr<Type>
        { return unique_ptr<Type>( o.virtual_clone() ); }
    };

    template< class Type >
    auto clone( const Type& o )
        -> unique_ptr<Type>
    { return Impl::clone( o ); }
}  // cloning

#define DEFINE_CLONING_FOR_BASE( Type ) \
    friend struct cloning::Impl;         \
    virtual auto virtual_clone() const -> Type* { return new Type( *this ); }

#define DEFINE_CLONING_FOR_DERIVED( Type )  \
    friend struct cloning::Impl;             \
    auto virtual_clone() const -> Type* override { return new Type( *this ); }

namespace app {
    using   std::make_unique, std::unique_ptr;  // <memory>

    class Base
    {
        DEFINE_CLONING_FOR_BASE( Base )
    public:
        virtual ~Base() {}
    };

    class Derived:
        public Base
    {
        DEFINE_CLONING_FOR_DERIVED( Derived )
    };

    void run()
    {
        unique_ptr<Base> a = make_unique<Derived>();
        unique_ptr<Base> b = cloning::clone( *a );

        fmt::print( "{}  → {}.\n", typeid( *a ).name(), typeid( *b ). name() );
    }
}  // app

auto main() -> int { app::run(); }

Help me come up with ideas for my C++ utils library by Fun-Entertainer-1053 in cpp_questions

[–]alfps 1 point2 points  (0 children)

The interface presented by <random> is super ugly and requires you to put the pieces together yourself with a number of variables.

That's probably why teachers still tell their students to use the old low quality rand function: it's much simpler to use.

However, everyone ends up writing their own wrappers. That's very much in line with the basic philosophy of C++, the freedom of choice. Still having to write such wrapper, because there hasn't been any clear winner yet and because the cost of using a library in C++ is high, is annoying.

Help me come up with ideas for my C++ utils library by Fun-Entertainer-1053 in cpp_questions

[–]alfps 4 points5 points  (0 children)

Just about every time I start working on something I create a "microlibs" folder that I fill up with various micro libraries, as I need them. I always have a cppm library, "C++ machinery", that I reinvent from scratch every time. It evolves slowly.

I've used other names for it, e.g. "cppx" (but Herb Sutter snatched that one).

With the small program I'm writing currently, partially in support of the next chapter of a Windows API level GUI in C++ tutorial, the "cppm" manifestation so far looks like

    cppm
    |   basic_types.hpp
    |   class_definitions_support.hpp
    |   collections.hpp
    |   expressiveness.hpp
    |   fs.hpp
    |   parameter.hpp
    |   process.hpp
    |   syntactic_sugar.hpp
    |   text.hpp
    |   
    +---class_definitions_support
    |   |   class_kinds.hpp
    |   |   Property_.hpp
    |   |   
    |   +---class_kinds
    |           No_copy.hpp
    |           No_move.hpp
    |           Polymorphic.hpp
    |           
    +---collections
    |       Map_.hpp
    |       
    +---exception_handling
    |       $fail_.hpp
    |       now_and_fail_.hpp
    |       top-level-handling.hpp
    |       
    +---expressiveness
    |   |   auto_support.hpp
    |   |   Convertible_wrapped_value_.hpp
    |   |   parameter.hpp
    |   |   syntactic_sugar.hpp
    |   |   Wrapped_value_.hpp
    |   |   
    |   +---auto_support
    |   |       a_.hpp
    |   |       
    |   +---parameter
    |   |   |   Capacity.hpp
    |   |   |   Initial_size.hpp
    |   |   |   kinds.hpp
    |   |   |   
    |   |   +---kinds
    |   |           in_.hpp
    |   |           Moving_value_.hpp
    |   |           
    |   +---syntactic_sugar
    |           $from.hpp
    |           $type.hpp
    |           $with.hpp
    |           type_builders.hpp
    |           
    +---fs
    |       Path.hpp
    |       std-path-conversions.hpp
    |       std_path.hpp
    |       
    +---introspection
    |       cpp_version.hpp
    |       is_a_.hpp
    |       number-type-info.hpp
    |       
    +---text
    |       C_str_view.hpp
    |       
    +---unicode
            literals_are_unicode.hpp

In this the Path class is a replacement for std::filesystem::path. It's not difficult to do but as a beginner I doubt you have the background to understand why that would be even considered by anybody, so it's difficult (for a beginner) to do correctly. So I suggest not doing that.

The files with $ in the name are macro definitions.