Inicjalizacja stałej tablicy statycznej w stylu C

Inicjalizacja stałej tablicy statycznej w stylu C
ReallyGrid
  • Rejestracja: dni
  • Ostatnio: dni
1

Cześć, wczoraj spotkałem się z takim problemem i do dziś rozwiązałem to w taki sposób. No nie wiem czy da się to lepiej rozwiązać, przy założeniach, które muszę spełnić. Może ktoś będzie miał jakiś fajny sprytny pomysł. O co chodzi.
To jest uproszczony schemat klas.
Mam strukturę Foo i klasę Bar, która jest immutable i przechowuje z góry określoną ilość swoich obiektów w tablicy statycznej S_BARS. Dodatkowych obiektów klasy Bar poza tymi w tejże tablicy S_BARS nie wolno tworzyć (taki ustalony zbiór singletonów).
A każdy zaś obiekt tej klasy przechowuje tablicę wskaźników na strukturę Foo (która może być również pusta).
W rzeczywistym problemie obiektów klasy Foo również nie można tworzyć samemu i też tworzą ustalony zbiór singletonów ale nie będę zaciemniał problemu, wiec uznajmy, że Foo wygląda jak wygląda.
Plik main.cpp

Kopiuj
struct Foo {
    Foo(int val) : value(val) {}
    int getValue() const { return value; }
private:
    const int value;
} foo1(1), foo2(2), foo3(3);

class Bar {
public:
    static const Bar& instance(int i) { return S_BARS[i]; }
    Bar(const Bar& obj) = delete;  /*  Zeby byla tylko jedna inst. na kazdy obiekt Bar */

    int count() const { return m_count; }
    const Foo& foo(int i) const { return *m_foos[i]; }

private:
    Bar(int count, const Foo** pFoos) : m_count(count), m_foos(pFoos) {}  /* K-tor prywatny */

private:
    static const Bar S_BARS[];

    const int m_count;   // ilosc elementow w m_foos ponizej;
    const Foo** m_foos;
};

const Foo* pFooNull[] = {};
const Foo* pFoo1   [] = {&foo1};
const Foo* pFoo3   [] = {&foo1, &foo2, &foo3};

const Bar Bar::S_BAR[] = { {0, pFooNull}, {1, pFoo1}, {3, pFoo3} };

I chodzi o to, że o ile ten kod działa poprawnie, to nie specjalnie podoba mi się to, że aby zainicjalizować zmienną S_BARS (w linii 30), to muszę użyć dodatkowych zmiennych
const Foo* pFooNull[] = {};
const Foo* pFoo1 [] = {&foo1};
const Foo* pFoo3 [] = {&foo1, &foo2, &foo3};

Inaczej mówiąc chciałbym żeby linia 30 wyglądała np. tak:
const Bar Bar::S_BARS[] = { {0, {}}, {1, {&foo1}}, {3, {&foo1, &foo2, &foo3}} };
Bo tych zmiennych w ogóle nie potrzebuję.

ROZWIĄZANIE NR 2
Można też użyć zamiast tablicy wskaźników const Foo** m_foos; wektora std::vector<const Foo*> m_foos; i nieznacznie zamieniając k-tor na
Bar(int count, const std::vector<const Foo*>& vFoos) : m_count(count), m_foos{vFoos} {} da się już zainicjalizować tę stałą S_BARS w sposób jaki oczekuję.

Tylko pytanie jest takie, czy można właśnie to zrobić używając tablicy w stylu C (i to jeszcze bez konstruktów typu new)?

MarekR22
  • Rejestracja: dni
  • Ostatnio: dni
0

Użyj po prostu std::span.
...
https://godbolt.org/z/jjxaeoaYa

Swoja droga jak widzę singleton, ze statyczną tablicą wskaźników na zmienne globalne, to odczuwam spory niepokój.

ReallyGrid
  • Rejestracja: dni
  • Ostatnio: dni
0

Dzięki, choć mam jeszcze jeden mały błąd. Próbuję dokończyć to co mi pokazałeś w taki sposób:

Kopiuj
const std::span<const Foo*> pFooNull;

const Bar Bar::S_BARS[] = { {pFooNull}, {{&foo1, &foo2}}, {{&foo1, &foo2, &foo3}} };

bo o to właśnie mi chodzi żeby pozbyć się niepotrzebnie zmiennych pFoo1, pFoo3, ...

Dostaję błąd (

Kopiuj
<source>:34:76: error: could not convert '{{(& foo1)}}' from '<brace-enclosed initializer list>' to 'Bar'
   34 | const Bar Bar::S_BARS[] = { {pFooNull}, {{&foo1}}, {{&foo1, &foo2, &foo3}} };
      |                                                                            ^
      |                                                                            |
      |                                                                            <brace-enclosed initializer list>
<source>:34:76: error: could not convert '{{(& foo1), (& foo2), (& foo3)}}' from '<brace-enclosed initializer list>' to 'Bar'
   34 | const Bar Bar::S_BARS[] = { {pFooNull}, {{&foo1}}, {{&foo1, &foo2, &foo3}} };
      |                                                                            ^
      |                                                                            |
      |                                                                            <brace-enclosed initializer list>

Rozumiem obawę lecz te zmienne globalne są także singletonami. Dlatego muszę je umieścić w tablicy statycznej właśnie poprzez wskaźnik na adres.

MarekR22
  • Rejestracja: dni
  • Ostatnio: dni
1

IMHO źle zadajesz pytanie.
Zamiast "napraw moje rozwiązanie tajemniczego problemu," powinieneś opisywać jaki problem ma rozwiązywać kod.
W tej chwili pytanie cierpi na problem XY.

ReallyGrid
  • Rejestracja: dni
  • Ostatnio: dni
0

Myślę jednak że pytanie jest dobrze sformułowane. Ale powtórzę i spróbuję krócej. Może zbyt długi post zaciemnił o co mi chodzi.

Mam takie pole klasowe:

Kopiuj
static const Bar S_BARS[];

i w pliku *.cpp chcę zainicjalizować tę zmienną.
Wykombinowałem jak to zrobić ale moje rozwiązanie nie podoba mnie się bo potrzebuję zdefiniować kilka zmiennych nazwanych tymczasowych, które są wykorzystane tylko do zainicjalizowania powyższej zmiennej.
Intuicyjnie uważam to za niepotrzebne tworzenie kolejnych bytów. Bo skoro da się ją zainicjalizować jak podałem w pierwszym rozwiązaniu w pierwszym poście:

Kopiuj
const Bar Bar::S_BAR[] = { {0, pFooNull}, {1, pFoo1}, {3, pFoo3} };

to myślę, że dobrze byłoby przypisać zawartość tych zmiennych w to miejsce:

Kopiuj
const Bar Bar::S_BARS[] = { {0, {}}, {1, {&foo1}}, {3, {&foo1, &foo2, &foo3}} };

Ale okazuje się, że kompilatorowi (używam GCC z -std=c++17) to się nie podoba.

Nie szukam rozwiązania, bo rozwiązanie mam. Pytam raczej o to czy można ten kod zrefaktoryzować do postaci, która oczekuję. Jeśli nie (bo np. c++ jest tak zdefiniowany i koniec), no to nie. Ale nie chciałbym wprowadzać dodatkowych obiektów tylko może wystarczyłoby inaczej zapisać typy. W końcu jak by nie patrzeć, to są podstawowe operacje z wykorzystaniem tablic i wskaźników. Poziom akademicki to jest, myślę, za dużo powiedziane.
Rozwiązanie z std::span ciekawe. Nigdy nie korzystałem z tego. Teraz przeczytałem na cppreference o tym ale kiedy próbuję dokończyć przykład, który mi podsunąłeś kompilator się buntuje. No sorry nie jestem alfą i omegą. Pierwszy raz widzę na oczy konstrukt z std::span

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.