#Trouble with template specialization.

48 messages · Page 1 of 1 (latest)

stone jetty
#

Using the uh implicit? constructors was working until I introduced the template specialization for rectangle here:
https://onlinegdb.com/8OGEQXZ-J

Is inheritance a good approach here? I normally shy away from it, but I don't want to redefine all the members on the specialized type.

fleet boneBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

edgy thicket
#

there's really no need for IntRectangle to have a distinct name

#

also your use of concepts is wonky

#

arithmetic is defined as std::integral<T> || std::floating_point<T>, so if you have a constraint that is just std::integral, this will be more constrained than arithmetic

stone jetty
#

(didn't realize the names wouldn't collide) But what's the right way to constrain to just integers in the specialization?

edgy thicket
#
template<typename T>
concept arithmetic  = std::integral<T> || std::floating_point<T>;

template<arithmetic P, arithmetic  S>
struct RectangleBase { /* base functionality */ }

// primary template
template<arithmetic P, arithmetic S>
struct Rectangle : RectangleBase<P, S> {};

// partial specialization
template<std::integral P, std::integral S>
struct Rectangle<P, S> : RectangleBase<P, S> {
    [[nodiscard]] std::ranges::range auto getPoints() const { /* ... */ }
};
stone jetty
#

let me try that out

#

that certianly looks nicer, but still getting the same issue with it not I dunno, looking in to the types like it was previoiusly able to. Hmm... https://onlinegdb.com/wOnbftgTT

edgy thicket
#

are we talking about the same error

#
main.cpp:17:47: error: could not convert ‘2’ from ‘int’ to ‘OpenXcom::Point’
   17 |     auto rectangle = OpenXcom::GridRectangle({2, 2}, {3, 5}).getBounds();
      |                                               ^
      |                                               |
      |                                               int
stone jetty
#

yeah

#

this works but I'd prefer to be able to use some magic here:
OpenXcom::GridRectangle{OpenXcom::GridPoint{2, 2}, OpenXcom::GridSize{3, 5}}

#

do I need to switch over to specifying the points/size via template instead?

edgy thicket
#

the whole thing falls apart because I guess you're doing CTAD (class template argument deduction) which deduces something from inside of an aggregate that is a constructor parameter

#

honestly dunno; the whole thing is wonky and weird

#

initializer lists and constructors don't work together that nicely

#

oh wait hold on, GridRectangle doesn't even do CTAD

#

because it's Rectangle<int, int>

#

yeah well, I think you can't keep it an aggregate then because the first {} will apply to the base class

#

just swallow this bitter pill and add a constructor I guess; it's the lesser evil here, and being able to use inheritance in the definition simplifies things a lot

stone jetty
#

yeah that did it, thanks!

fleet boneBOT
#

Thank you and let us know if you have any more questions!

This thread is now set to auto-hide after an hour of inactivity

cold grove
# stone jetty yeah that did it, thanks!

just in case you ever decide to try building something similar to that grid in the future, but in a way that leans on template features a bit more, i can toss a random snippet in here that i usually keep around for whenever i need to work through what the compiler(s) like best for type/size deduction in 2D / 3D arrays...

#

;compile -std=c++23

#include <array>
#include <concepts>
#include <memory>
#include <print>
#include <ranges>
#include <string_view>
#include <utility>
#include <vector>

template <typename T, std::integral auto N, std::integral auto... M>
struct Grid {
public:
    constexpr explicit Grid(std::initializer_list<T[N]> rows) {
        for (auto&& [i, elem] : rows | std::views::join | std::views::enumerate)
            m_cells[i] = elem;
    }

    constexpr auto rows() const {
        return N;
    }

    constexpr auto columns() const {
        return N;
    }

    constexpr const auto& operator[](std::pair<std::size_t, std::size_t> idx) const {
        auto&& [row, col] = idx;
        const auto index{ (row * this->columns()) + col };
        return m_cells[index];
    }

private:
    std::array<T, N * N> m_cells{};
};

void print_grid(auto&& grd) {
    for (std::size_t row : std::views::iota(0, grd.rows())) {
        for (std::size_t col : std::views::iota(0, grd.columns())) {
            auto&& val = grd[{ row, col }];
            std::print("{}{}", col == 0 ? '\n' : ' ', val);
        }
    }
    std::println();
}

int main() {
    const Grid numsA{
        { 1, 2, 3 },
        { 4, 5, 6 },
        { 7, 8, 9 },
    };

    const Grid numsB{ {
        { 1, 3, 3 },
        { 7, 1, 3 },
        { 7, 7, 1 },
    } };

    const Grid alph1{
        { "A1", "B1", "C1", "D1" },
        { "A2", "B2", "C2", "D2" },
        { "A3", "B3", "C3", "D3" },
        { "A4", "B4", "C4", "D4" },
    };

    const Grid alph2{ {
        { "A1", "B1", "C1", "D1" },
        { "A2", "B2", "C2", "D2" },
        { "A3", "B3", "C3", "D3" },
        { "A4", "B4", "C4", "D4" },
    } };

    print_grid(numsA);
    print_grid(numsB);
    print_grid(alph1);
    print_grid(alph2);
}
thorny burrowBOT
#
Program Output
1 2 3
4 5 6
7 8 9

1 3 3
7 1 3
7 7 1

A1 B1 C1 D1
A2 B2 C2 D2
A3 B3 C3 D3
A4 B4 C4 D4

A1 B1 C1 D1
A2 B2 C2 D2
A3 B3 C3 D3
A4 B4 C4 D4
cold grove
#

it just in whatever state i left it in last time i used it for something, so don't trust it too much haha

#

i usually just use it to get something minor implemented then toss it aside for another day

#

i'm pretty sure this version only supports symmetric dimension sizes

stone jetty
#

I appreciate it. I'm using a fixed size, binary grid though. IE:

template<size_T WIDTH, size_T HEIGHT>
class GridSpace {
    std::bitset<WIDTH * HEIGHT> _cells;
}
#

the main point of this grid is to check for overlapping items in on a fixed size field.

cold grove
#

np. ah ok i see, yeah that's probably a good idea.... sometimes it can be quite tricky to figure out how to write something in a way that the compiler can deduce it all for different data types and/or dimensions

stone jetty
#

Think resident evil. Every item has a given shape that can be represented by a grid. And I can just bit mash them to check for overlaps.

cold grove
#

haha, are you talking about the inventory management in that game

#

they always made those too small lol

#

makes sense though

stone jetty
#

yeah. I think using a binary grid is a lot more foolproof (if less intuitive) then trying to manage lists of points or whatever.

cold grove
#

yeah seems like a reasonable idea as long as the grid doesn't get too big

#

i.e. if it's 1,000,000 x 1,000,000 units in size, then you're better off just using something like a quadtree/r-tree

#

but i have a feeling it's not going to be tha tbig haha

stone jetty
#

how big would be to big? 🤔 If this works out, I might consider applying the strategy to map elements...

Ah, yeah, that would be to big 😛

cold grove
#

i wouldn't worry about it until it becomes a slowdown or uses too much memory as-is

stone jetty
#

sure, I'll burn that bridge when I come to it. 🔥

cold grove
#

profilers will usually give you a clear answer if things start feeling sluggish or too resource intensive in general (for any program/code, not even in this specific context)

#

yeah, that's pretty much how you should treat optimizing things too early, especially while the codebase is still newer and more volatile in general

#

whenever things start to feel slugglish a profiler will just tell you most of what you'd care about so you can optimize the biggest bottlenecks and/or low hanging fruit

#

(something like vtune for intel cpus in case you've never used one before)