RandomWalkers3D Example Code



  • Hallo,
    hier ein Beispiel für die colorierten und gezeichneten Wege von mehreren Random Walkers in einem drehenden 3D-Würfel.
    Ich denke, man kann den Code verstehen, ohne zu wissen, was die zp::DotEngine macht. Es wird zwar von ihr geerbt, aber für deren Funktionen braucht man nicht das Verständnis für.

    Ich teile den Code in drei Teile auf. Ein Post je Teil:

    • diese Einleitung und den 'Description Header'. In diesem werden notwendigen Variablen erstellt und Werte zugewiesen. In der Entwicklung kann man auch an diesem Ort editieren.
    • die .h-Datei. Mit einigen constexpr Methoden. Einige, die Update-Methoden sind hier definiert. Ich denke, das ist die beste Methode für constexpr-Deklaration. Dann trennt sich auch die Streu vom Weizen, was nicht constexpr ist, läuft dann auch nicht.
    • die .cpp-Datei. Hier habe ich einige constexpr Methoden definiert, hauptsächlich um nicht so viel Code auf einer Seite zu haben, außerdem sind es nur Setup-Methoden, die nur einmal aufgerufen werden.

    So weit, so gut. Dann schauen wir mal...
    .

    #pragma once
    #include <zp/Core/Common.h>
    
    namespace zp
    {
        // node start position
        enum
        {
            CENTER = 0,
            LEFT,
            RIGHT,
            TOP,
            BOTTOM,
            FRONT,
            BACK,
            RANDOM
        };
    
        // how to render node
        enum
        {
            NONE = 0,
            WHITE_POINT,
            COLORED_POINT,
            WHITE_NODE,
            COLORED_NODE
        };
    
        // definition of node & walker
        struct Node3D
        {
            Vector3D<double> Pos, Vel;
            double Radius = 0;
            double Heading = 0;
            RGBColor Color;
    
            bool IsDone = false;
        };
    
        struct Walker3D
        {
            Node3D Active, Start, End;
            std::vector<Node3D> Trace;
        };
    
        // description & setup
        struct RandomWalkersDesc
        {
            int FOV = 30; // field of view
            int CubeScaled = 100;
            Coord CubePosition = { 10, 0 }; // from center
            int CubeDistance = 330;
    
            static constexpr int NUM_OF_WALKERS = 50;
           
            double NodeRadius = 2.0;
            double NodeHeading = 0.2;
            double NodeScaler = 0.002; // ratio between cube side and node radius
    
            int NodeStartPosID = CENTER;
            int NodeMode = WHITE_NODE;
            int TraceMode = COLORED_NODE;
       
    
            bool ShowGenerate = false;
            bool StartWalking = true;
            bool AutoRotate = true;
    
            bool GenerateLoop = true;
            int LoopDelay = 75;
    
            int PaletteSize = 1000;
            std::vector<RGBColor> Palette;
    
        };
    
        using RandomWalkers3D = std::array<Walker3D, RandomWalkersDesc::NUM_OF_WALKERS>;
    }
    
    


  • #pragma once
    #include <zp/Engine/DotEngine.h>
    #include <zp/Models/Cube.h>
    #include "Headers/ModelView3D.h"
    #include "Headers/TransformObject.h"
    #include "Headers/CoordinateAxis3D.h"
    
    #include "Headers/RandomWalkers3DDesc.h"
    
    namespace zp
    {
        class RandomWalker3DX : public DotEngine
        {
        public:
            RandomWalker3DX();
            RandomWalker3DX(const int windowWidth, const int windowHeight);
            RandomWalker3DX(const int windowWidth, const int windowHeight, const int dotWidth, const int dotHeight);
            RandomWalker3DX(const ScreenResDesc& desc);
            ~RandomWalker3DX();
    
        private:
            // side of Cube is { -0.5, 0.5 }, side half is 0.5
            static constexpr double BORDER = Cube3D<double>::SIDE_HALF(); 
    
            ModelView3D<double> View;
            TransformObject<double> Global;
            CoordinateAxis3D<double> Axes3D;
    
            RandomWalkers3D Walkers;
            RandomWalkersDesc Desc;
    
            std::size_t TotalNodes = 0;
            std::size_t WalkersDone = 0;
            bool Generating = false;
            float GenTime = 0;
    
        private:
            void setupCanvas();
            virtual bool onCreate() override;
            virtual bool onUpdate() override;
            virtual bool onInput() override;
    
        public:
            constexpr void setupWalkers(RandomWalkers3D& walkers);
    
            constexpr void createModelView(const int fovDegree);
            constexpr void createGlobal();
            constexpr void createBorderCube();
            constexpr void createAxes3D();
            constexpr void createColorPalette(const int size);
    
            constexpr void createWalkers(RandomWalkers3D& walkers);
            constexpr void resetWalkers(RandomWalkers3D& walkers);
    
            void createWalker(Walker3D& walker);
            void resetWalker(Walker3D& walker);
    
            constexpr void printInfo(const RandomWalkers3D& walkers);
    
        private:
            void setRandomStartPosition(Node3D& node) const;
            Vector3D<double> getRandomVelocityInt(const double scale) const;
            Vector3D<double> getRandomVelocity(const double heading, const double scale) const;
            Vector3D<double> getRandomAngle(const double heading) const;
    
        private:
            
            // Generation ////////////////////////////////////////////////////////////////////////////////
          
            constexpr void generateWalker(Walker3D& walker, const int renderID)
            {
                if (walker.Active.IsDone)
                    return;
    
                if (isPointInsideBorders(walker.Active.Pos))
                {
                    walker.Active.Vel *= Mat4x4<double>().makeRotation(getRandomAngle(walker.Active.Heading));
                    walker.Active.Pos += walker.Active.Vel;
    
                    walker.Active.Color = getNodeColor(walker.Active.Pos);
                    drawNode(walker.Active, renderID);
    
                    walker.Trace.emplace_back(walker.Active);
                    walker.End.Pos = walker.Active.Pos;
    
                    TotalNodes++;
                }
                else
                {
                    walker.Active.Vel.clear();
                    walker.Active.IsDone = true;
    
                    WalkersDone++;
                    if (WalkersDone >= std::size(Walkers))
                        Generating = false;
                }
            }
    
            constexpr void generateWalkers(RandomWalkers3D& walkers)
            {
                for (auto& walker : walkers)
                    generateWalker(walker, Desc.NodeMode);
            }
    
            constexpr void updateWalkers(RandomWalkers3D& walkers)
            {
                if (Generating)
                {
                    generateWalkers(walkers);
                }
    
                if (!Generating || Desc.ShowGenerate)
                {
                    drawWalkers(walkers, Desc.TraceMode);
    
                    if (Desc.GenerateLoop)
                    {
                        if (delayThis(Desc.LoopDelay))
                            resetWalkers(walkers);
                    }
                }
            }
    
            // Projection //////////////////////////////////////////////////////////////////////////////////////////
    
            constexpr Vector3D<double> projectedPoint(const Vector3D<double>& p) const
            {
                Vector3D<double> point = p;
    
                point.transform(Global.matrix());
                point = View.projectedToScreen(point);
    
                return point;
            }
    
            constexpr double projectedLength(const double length) const
            {
                TransformObject<double> local = Global;
                local.rotate().clear();
    
                Vector3D<double> pointA;
                pointA.transform(local.matrix());
                pointA = View.projectedToScreen(pointA);
    
                Vector3D<double> pointB = { length, 0.0, 0.0 };
                pointB.transform(local.matrix());
                pointB = View.projectedToScreen(pointB);
    
                return pointB.x - pointA.x;
            }
    
            constexpr Node3D projectedNode(const Node3D& n) const
            {
                Node3D node = n;
    
                node.Pos = projectedPoint(node.Pos);
                node.Radius = projectedLength(node.Radius);
    
                return node;
            }
    
            constexpr std::vector<Node3D> projectedWalkers(const RandomWalkers3D& walkers) const
            {
                std::vector<Node3D> allNodes;
    
                for (const auto& walker : walkers)
                {
                    std::vector<Node3D> nodes = walker.Trace;
    
                    for (auto& node : nodes)
                        node = projectedNode(node);
    
                    if (Desc.ShowGenerate)
                    {
                        nodes.insert(nodes.begin(), projectedNode(walker.Start));
                        nodes.emplace_back(projectedNode(walker.End));
                    }
    
                    allNodes = utils::add(allNodes, nodes);
                }
    
                std::sort(allNodes.begin(), allNodes.end(), [](const auto& p1, const auto& p2)
                    {
                        return p1.Pos.z > p2.Pos.z;
                    });
    
                return allNodes;
            }
    
            constexpr std::vector<Box<double>> projectedBoxes() const
            {
                std::vector<Box<double>> boxes(std::size(Cube3D<double>::BOX_MESH()));
    
                auto n = 0;
                for (const auto& b : Cube3D<double>::BOX_MESH())
                {
                    Box<double> box = b;
                    box.transform(Global.matrix());
                    box.color() = DARK_GREY;
    
                    if (View.isVisibleToCamera(box))
                        box.color() = GREY;
    
                    box = View.projectedToScreen(box);
                    boxes[n++] = box;
                }
    
                std::sort(boxes.begin(), boxes.end(), [](const auto& c1, const auto& c2)
                    {
                        return c1.center().z > c2.center().z;
                    });
    
                return boxes;
            }
    
            ////////////////////////////////////////////////////////////////////////////////////////////////////////
    
            constexpr RGBColor getNodeColor(const Vector3D<double>& pos) const
            {
                std::size_t index = 0;
                double radius = 0;
    
                switch (Desc.NodeStartPosID)
                {
                case CENTER:
                    radius = math::toSpherical(pos).Radius;
                    index = static_cast<std::size_t>(utils::map(radius, 0.0, BORDER * 1.3, 0.0, std::size(Desc.Palette) - 1.0));
                    return Desc.Palette[index];
                case LEFT:
                    index = static_cast<std::size_t>(utils::map(pos.x, -BORDER, BORDER, 0.0, std::size(Desc.Palette) - 1.0));
                    return Desc.Palette[index];
                case RIGHT:
                    index = static_cast<std::size_t>(utils::map(pos.x, BORDER, -BORDER, 0.0, std::size(Desc.Palette) - 1.0));
                    return Desc.Palette[index];
                case TOP:
                    index = static_cast<std::size_t>(utils::map(pos.y, -BORDER, BORDER, 0.0, std::size(Desc.Palette) - 1.0));
                    return Desc.Palette[index];
                case BOTTOM:
                    index = static_cast<std::size_t>(utils::map(pos.y, BORDER, -BORDER, 0.0, std::size(Desc.Palette) - 1.0));
                    return Desc.Palette[index];
                case FRONT:
                    index = static_cast<std::size_t>(utils::map(pos.z, -BORDER, BORDER, 0.0, std::size(Desc.Palette) - 1.0));
                    return Desc.Palette[index];
                case BACK:
                    index = static_cast<std::size_t>(utils::map(pos.z, BORDER, -BORDER, 0.0, std::size(Desc.Palette) - 1.0));
                    return Desc.Palette[index];
                case RANDOM:
                    radius = math::toSpherical(pos).Radius;
                    index = static_cast<std::size_t>(utils::map(radius, BORDER * 1.3, 0.0, 0.0, std::size(Desc.Palette) - 1.0));
                    return Desc.Palette[index];
                default:
                    return GREY;
                }
            }
    
            constexpr void drawNode(const Node3D& node, const int renderID) const
            {
                switch (renderID)
                {
                case NONE:
                    break;
                case WHITE_POINT:
                    drawPoint(projectedPoint(node.Pos), WHITE);
                    break;
                case COLORED_POINT:
                    drawPoint(projectedPoint(node.Pos), node.Color);
                    break;
                case WHITE_NODE:
                    drawFillCircle(projectedPoint(node.Pos), projectedLength(node.Radius), WHITE);
                    break;
                case COLORED_NODE:
                    drawFillCircle(projectedPoint(node.Pos), projectedLength(node.Radius), node.Color);
                    break;
                default:
                    break;
                }
            }
    
            constexpr void drawWalkers(const RandomWalkers3D& walkers, const int renderID) const
            {
                switch (renderID)
                {
                case NONE:
                    break;
                case WHITE_POINT:
                    for (const auto& node : projectedWalkers(walkers))
                        drawPoint(node.Pos, WHITE);
                    break;
                case COLORED_POINT:
                    for (const auto& node : projectedWalkers(walkers))
                        drawPoint(node.Pos, node.Color);
                    break;
                case WHITE_NODE:
                    for (const auto& node : projectedWalkers(walkers))
                         drawFillCircle(node.Pos, node.Radius, WHITE);
                    break;
                case COLORED_NODE:
                    for (const auto& node : projectedWalkers(walkers))
                        drawFillCircle(node.Pos, node.Radius, node.Color);
                    break;
                default:
                    break;
                }
            }
    
            constexpr void updateBorderCube()
            {
                if (Global.translate().z < Desc.CubeDistance + 15.0)
                    drawCoordinateAxes();
    
                drawBorderCube();
            }
    
            constexpr void drawBorderCube() const
            {
                for (const auto& box : projectedBoxes())
                    drawBox(box, box.color());
            }
    
            constexpr void drawCoordinateAxes()
            {
                Axes3D.transform(Global.matrix());
    
                const int tmp = lineWeight();
                lineWeight(2);
                Axes3D.draw(*this, { 0, 0 }, 1); // to do
                lineWeight(tmp);
            }
    
            constexpr bool isPointInsideBorders(const Vector3D<double>& point, const double delta = 0.0) const
            {
                const double min = -BORDER + delta;
                const double max = BORDER - delta;
    
                return (point.x >= min && point.x <= max &&
                    point.y >= min && point.y <= max &&
                    point.z >= min && point.z <= max);
            }
    
            constexpr bool isPointOutsideBorders(const Vector3D<double>& point, const double delta = 0.0) const
            {
                return !isPointInsideBorders(point, delta);
            }
        };
    }
    
    


  • #include "RandomWalkers3DX.h"
    #include <zp/Math/Random.h>
    
    zp::RandomWalker3DX::RandomWalker3DX()
    {
    }
    
    zp::RandomWalker3DX::RandomWalker3DX(const int windowWidth, const int windowHeight) :
    	DotEngine(windowHeight, windowHeight)
    {
    }
    
    zp::RandomWalker3DX::RandomWalker3DX(const int windowWidth, const int windowHeight, const int dotWidth, const int dotHeight) :
    	DotEngine(windowWidth, windowHeight, dotWidth, dotHeight)
    {
    }
    
    zp::RandomWalker3DX::RandomWalker3DX(const ScreenResDesc& desc) :
    	DotEngine(desc)
    {
    }
    
    zp::RandomWalker3DX::~RandomWalker3DX()
    {
    }
    
    void zp::RandomWalker3DX::setupCanvas()
    {
    	screenColor() = VERY_DARK_GREY;
    	screenFont().setAppearance({ 1, 2 }, 1);
    	screenFont().color() = BLACK;
    
    	showFPS();
    }
    
    bool zp::RandomWalker3DX::onCreate()
    {
    	setupCanvas();
    	setupWalkers(Walkers);
    
    	return true;
    }
    
    bool zp::RandomWalker3DX::onUpdate()
    {
    	updateWalkers(Walkers);
    	updateBorderCube();
    	printInfo(Walkers);
    
    	return true;
    }
    
    bool zp::RandomWalker3DX::onInput()
    {
    	// exit
    	if (getKey(VK_ESCAPE).Pressed)
    		return false;
    
    	// reset walkers
    	if (getKey(VK_BACK).Pressed)
    	{
    		resetWalkers(Walkers);
    	}
    
    	// reset transformation
    	if (getKey(VK_RETURN).Pressed)
    	{
    		createGlobal();
    	}
    
    	// auto rotate
    	if (getKey(VK_SPACE).Released)
    	{
    		Desc.AutoRotate = !Desc.AutoRotate;
    	}
    
    	if (Desc.AutoRotate)
    	{
    		Global.rotate().x += 0.4 * elapsedTime();
    		Global.rotate().y += 0.5 * elapsedTime();
    		Global.rotate().z += 0.3 * elapsedTime();
    	}
    
    	// free hand transform 
    	Global.translateXY(*this, M_LEFT, 3.0);
    	Global.translateZ(*this, 'A', 'Y', 2.0);
    
    	Global.rotateXY(*this, M_RIGHT, 2.0);
    	Global.rotateZ(*this, 'Q', 'W', 1.0);
    
    	return true;
    }
    
    constexpr void zp::RandomWalker3DX::setupWalkers(RandomWalkers3D& walkers)
    {
    	createModelView(Desc.FOV);
    	createGlobal();
    	createBorderCube();
    	createColorPalette(Desc.PaletteSize);
    	createWalkers(walkers);
    }
    
    constexpr void zp::RandomWalker3DX::createModelView(const int fovDegree)
    {
    	View = { screenRect(), static_cast<float>(fovDegree) };
    }
    
    constexpr void zp::RandomWalker3DX::createGlobal()
    {
    	const Vector3D<double> scale(Desc.CubeScaled, Desc.CubeScaled, Desc.CubeScaled);
    	const Vector3D<double> rotate(0, 0, 0);
    	const Vector3D<double> translate(Desc.CubePosition.X, Desc.CubePosition.Y, Desc.CubeDistance);
    
    	Global.setup(scale, rotate, translate);
    }
    
    constexpr void zp::RandomWalker3DX::createBorderCube()
    {
    	Cube3D<double>::SETUP();
    	createAxes3D();
    }
    
    constexpr void zp::RandomWalker3DX::createAxes3D()
    {
    	Axes3D.setup(View);
    	Axes3D.createLength(0.1);
    	Axes3D.createColors(BLACK, BLACK, BLACK);
    }
    
    constexpr void zp::RandomWalker3DX::createColorPalette(const int size)
    {
    	Desc.Palette = utils::reMap(color::RGBPalette(), size);
    }
    
    constexpr void zp::RandomWalker3DX::createWalkers(RandomWalkers3D& walkers)
    {
    	for (auto& walker : walkers)
    		createWalker(walker);
    
    	Generating = true;
    }
    
    constexpr void zp::RandomWalker3DX::resetWalkers(RandomWalkers3D& walkers)
    {
    	for (auto& walker : walkers)
    		resetWalker(walker);
    
    	TotalNodes = 0;
    	WalkersDone = 0;
    	Generating = true;
    }
    
    void zp::RandomWalker3DX::createWalker(Walker3D& walker)
    {
    	walker.Active.Radius = Desc.NodeRadius * Desc.NodeScaler;
    	walker.Active.Heading = Desc.NodeHeading;
    	walker.Active.Color = GREY;
    
    	resetWalker(walker);
    }
    
    void zp::RandomWalker3DX::resetWalker(Walker3D& walker)
    {
    	walker.Trace.clear();
    	setRandomStartPosition(walker.Active);
    
    	walker.Active.IsDone = false;
    	walker.Start = walker.Active;
    	walker.Start.Radius *= 1.5;
    	walker.Start.Color = BLACK;
    	walker.End = walker.Start;
    	walker.End.Color = WHITE;
    }
    
    constexpr void zp::RandomWalker3DX::printInfo(const RandomWalkers3D& walkers)
    {
    	Walker3D walker = walkers[0];
    	Vector2D<int> pos = { 1, screenFont().getHeight() / 2 };
    
    	drawString(pos, "Node Radius   " + utils::toString(projectedLength(walker.Active.Radius), 2, 2));
    	pos.y += screenFont().getHeight();
    	drawString(pos, "Node Heading  " + utils::toString(walker.Active.Heading, 1, 2));
    	pos.y += screenFont().getHeight();
    	drawString(pos, "        Palette    " + std::to_string(std::size(Desc.Palette)));
    	pos.y += screenFont().getHeight();
    	drawString(pos, "       Walkers    " + std::to_string(std::size(walkers)));
    	pos.y += screenFont().getHeight() * 2;
    
    	drawString(pos, "Walkers Done     " + std::to_string(WalkersDone));
    	pos.y += screenFont().getHeight();
    	drawString(pos, "Total Nodes       " + std::to_string(TotalNodes));
    	pos.y += screenFont().getHeight();
    	drawString(pos, "Average Nodes   " + std::to_string(TotalNodes / std::size(walkers)));
    }
    
    void zp::RandomWalker3DX::setRandomStartPosition(Node3D& node) const
    {
    	Random rng;
    	Vector3D<double> pos, vel;
    	int posID = Desc.NodeStartPosID;
    
    	if (posID == RANDOM)
    		posID = rng.uniformInt<int>(1, 6);
    
    	switch (posID)
    	{
    	case CENTER:
    		pos.clear();
    		vel = getRandomVelocity(1.0, node.Radius * 2);
    		break;
    	case LEFT:
    		pos.x = -BORDER;
    		pos.y = rng.uniformReal<double>(-BORDER, BORDER);
    		pos.z = rng.uniformReal<double>(-BORDER, BORDER);
    		vel.x = node.Radius * 2;
    		break;
    	case RIGHT:
    		pos.x = BORDER;
    		pos.y = rng.uniformReal<double>(-BORDER, BORDER);
    		pos.z = rng.uniformReal<double>(-BORDER, BORDER);
    		vel.x = -node.Radius * 2;
    		break;
    	case TOP:
    		pos.x = rng.uniformReal<double>(-BORDER, BORDER);
    		pos.y = -BORDER;
    		pos.z = rng.uniformReal<double>(-BORDER, BORDER);
    		vel.y = node.Radius * 2;
    		break;
    	case BOTTOM:
    		pos.x = rng.uniformReal<double>(-BORDER, BORDER);
    		pos.y = BORDER;
    		pos.z = rng.uniformReal<double>(-BORDER, BORDER);
    		vel.y = -node.Radius * 2;
    		break;
    	case FRONT:
    		pos.x = rng.uniformReal<double>(-BORDER, BORDER);
    		pos.y = rng.uniformReal<double>(-BORDER, BORDER);
    		pos.z = -BORDER;
    		vel.z = node.Radius * 2;
    		break;
    	case BACK:
    		pos.x = rng.uniformReal<double>(-BORDER, BORDER);
    		pos.y = rng.uniformReal<double>(-BORDER, BORDER);
    		pos.z = BORDER;
    		vel.z = -node.Radius * 2;
    		break;
    	default:
    		pos.clear();
    		vel = getRandomVelocity(node.Heading, node.Radius * 2);
    	}
    
    	node.Pos = pos;
    	node.Vel = vel;
    }
    
    zp::Vector3D<double> zp::RandomWalker3DX::getRandomVelocityInt(const double scale) const
    {
    	Random rng;
    	auto p = false;
    
    	p = rng.uniformBool();
    	const auto x = p ? -1 : 1;
    	p = rng.uniformBool();
    	const auto y = p ? -1 : 1;
    	p = rng.uniformBool();
    	const auto z = p ? -1 : 1;
    
    	return Vector3D<double>(x, y, z) * scale;
    }
    
    zp::Vector3D<double> zp::RandomWalker3DX::getRandomVelocity(const double heading, const double scale) const
    {
    	Random rng;
    	const double angle = heading * math::TAU<double>;
    	const double phi = rng.uniformReal<double>(-angle / 2, angle / 2);
    	const double theta = rng.uniformReal<double>(-angle / 2, angle / 2);
    
    	return math::toCartesian<double>(scale, phi, theta);
    }
    
    zp::Vector3D<double> zp::RandomWalker3DX::getRandomAngle(const double heading) const
    {
    	Random rng;
    	const double angle = heading * math::TAU<double>;
    	const double x = rng.uniformReal<double>(-angle / 2, angle / 2);
    	const double y = rng.uniformReal<double>(-angle / 2, angle / 2);
    	const double z = rng.uniformReal<double>(-angle / 2, angle / 2);
    
    	return { x, y, z };
    }
    
    
    
    
    
    
    


  • Vielen Danke fürs Lesen! 🙂



  • @zeropage
    Sieht doch soweit ganz gut aus. Wenn es jetzt noch sauber dokumentiert wäre, wäre ich rundrum glücklich.

    Ein paar Kleinigkeiten.

    • Schaue dir mal std::ranges::sort an.
    • Du hast einige constexpr Funktionen. Möchtest du diese auch zur Compilerzeit einsetzen?
    • Einen leeren Destruktor würde ich entfernen.
    • Ein nacktes Enum würde ich nicht mehr nutzen, sondern eher ein enum class.
    • Du gehst sparsam mit Pointer um. Super!

    Ein Tipp: Schaue dir mal Cppcheck an. Dieses Programm dient zur statischen Codeanalyse und lehrt dich nebenbei auch neuere Syntax.



  • @Quiche-Lorraine sagte in RandomWalkers3D Example Code:

    Du hast einige constexpr Funktionen. Möchtest du diese auch zur Compilerzeit einsetzen?

    Vor allem die constexpr void member Functions wie setupWalkers, die aber ihre Paramter bearbeiten. Ist das richtig so?

    (Ich habe über 5 Jahre lang kein C++ mehr geschrieben, bin hier also etwas raus, aber das erscheint mir doch etwas merkwürdig)

    Edit: https://en.cppreference.com/w/cpp/language/constexpr.html
    Dort in Notes: "It is possible to write a constexpr function whose invocation can never satisfy the requirements of a core constant expression:" - hm... klingt komisch, aber warum?



  • Du kannst deinen Code auch mit ChatGPT-4o oder ChatGPT-5 prüfen lassen. Damit habe ich gute Erfahrungen gemacht, gerade auch hinsichtlich modernster Syntax.


Anmelden zum Antworten