Czy można zdefiniować statyczny kontener wewnątrz statycznej funkcji?

0

Czołem,
czy definicja takiego kontenera wewnątrz statycznej funkcji ma jakiś sens? Próbuje wypełnić kontener wartościami, ale się nie udaje, bo dostaje unresolved externals. Ręczne wrzucenie danych poprzez initializer_list na globalnym zasięgu działa, ale nie podoba mi się trzymanie tego w tym miejscu, dlatego wolałem zwrapować to do jakiejś funkcji. Tak swoją drogą na globalnym zasięgu nie mogę nawet pętli napisać, bo ona tam nie może być i teraz nawet nie wiem jak wrzucić dane do tego kontenera :P

std::vector<std::string> Game::provinces = {"poo", "foo"}; //no i to działa. Logiczne.
void Game::loadProvincesFromFile() { //statyczna metoda 
      std::fstream file("provinces.txt");
      if (!file) {
            std::cout << "couldn't load provinces!\n";
            exit(0);
      }
      else {
            std::string lineToRead;
            std::vector<std::string> Game::provinces; //cannot be defined in the current scope 
            while (getline(file, lineToRead)) 
                  Game::provinces.push_back(lineToRead);
      }
}

Pomóżcie, bo zaraz chyba osiwieje od tego keyworda static!
P.S.: sorki za ręczne formatowanie - piszę z komórki (na kompie nie mam neta, bo awaria w orange XD)

1

Przerzuć deklarację do pliku nagłówkowego.
https://wandbox.org/permlink/jU9MYQbe1C0PH49r
Gdy provinces zrobisz zmienną statyczną metody (https://wandbox.org/permlink/ckBatW6ZII7DiavW) to nie będziesz mógł się do niej normalnie dostać (chyba że ją w jakiś sposób zwrócisz).

0

Deklarację już miałem w pliku nagłówkowym, a nie działało :D wpadłem na workaround w postaci funkcji, która po prostu zwraca wypelniony tymczasowy wektor.
Definicja po zmianie wygląda tak:

std::vector<std::string> Game::provinces = Game::loadProvincesFromFile();

Jak zwykle rozwiązanie jest łatwe, a jak już na nie wpadnę to po zadaniu pytania na forum, eh...
W każdym razie dzięki.

1
Cyberah napisał(a):

Próbuje wypełnić kontener wartościami, ale się nie udaje, bo dostaje unresolved externals.

unresolved externals oznacza problem linkowania, a to zwykle oznacza, że problemem nie jest sam kod, ale jego organizacja w projekcie:

  • brakuje dołączonej biblioteki
  • brakuje źródła w projekcie
  • brakuje definicji symbolu
  • symbol jest źle zadeklarowany

Jako, że napisałeś tylko: unresolved externals lepszej odpowiedzi nie odstaniesz.Radzę wkleić pełny błąd i więcej kontekstu do kodu (na wszelki wypadek).

Sam koncept metody statycznej w tym kontekście jest dziwny, coś źle kombinujesz.

1

Co chcesz właściwie osiągnąć i dlaczego "nie podoba ci się trzymanie tego w tym miejscu"?

std::vector<std::string> Game::provinces;

void Game::loadProvincesFromFile() {
            ...
            while (getline(file, lineToRead)) 
                  Game::provinces.push_back(lineToRead);
}

w czym problem?

0

@MarekR22: Tak jest, tak namieszałem w kodzie, że dzisiaj nawet nie umiem odwzorować tego błędu.

Chciałem umieścić definicję statycznego membera w funkcji i kompilator mi na to nie pozwalał, a więc umieściłem pustą definicję gdzieś globalne, a inicjacje wrzuciłem do funkcji i przez to statyczny getter (jak i sama składowa) zostały pozostawione same sobie i przez to dostawałem LNK1120.

Już wiem, że nie da się tak zrobić, ale ciekawi mnie czy zmienne statyczne zawsze muszą tak wisieć w globalnym zasięgu? Nawet do namespace nie można ich wrzucić (member cannot be defined in the current scope).

0

Przecież to nie jest „globalnie”, tylko w Game.

A tak w ogóle, czy Game to klasa czy namespace? Bo jeśli klasa, to oznacz provinces jako private.
A jeśli namespace, to… coś robisz nie tak :-)

0

Game to klasa i składowa provinces jest prywatna. Nie wiem co ja mam z tym globalnym zasięgiem, po prostu źle mi się kojarzy i myślałem że takie definicje czy inne rzeczy powinny być jakoś spakowane w funkcji czy w innym, lokalnym zasięgu.

[Offtop] Tak swoją drogą na komórce nigdzie nie mogę znaleźć tego znaku zapytania przy tworzeniu postu z którego po rozwinięciu mogłem skopiować te ciapki aby oznaczyć kod. Ten znaczek jest gdzieś na klawiaturze czy to trzeba skopiować?

0

Użycie inicjalizacji Game::provinces w stylu

std::vector<std::string> Game::provinces = Game::loadProvincesFromFile();

będzie skutkowało brakiem możliwości obsługi wyjątków, które może wyrzucić funkcja loadProvincesFromFile()

Jest to o tyle istotne, że w przypadku braku pliku lub jego uszkodzenia cała aplikacja jest skazana na zamknięcie już na samym początku bez możliwości późniejszej obsługi tego rodzaju błędów w już działającej aplikacji.

0

@TomaszLiMoon: Dobra uwaga. Próbuję pisać kod exception free, ale pobieranie danych z pliku jest ciężką do napisania funkcją w której damy gwarancję, że nie rzuci wyjątkiem. Jest to praktycznie niemożliwe według mnie.

Tylko pojawia się pytanie czy to bezpieczne utrzymywać otwartą aplikację z uszkodzonym plikiem. Dlaczego uważasz że przy takiej inicjalizacji będzie brak możliwości obsługi wyjątków? W ostateczności myślę, że możnaby było napisać klasę dziedziczącą po std::exception.

0

Wszystko fajnie - ale wytłumacz nam jedną ważną rzecz - DLACZEGO chcesz żeby to była składowa statyczna ?

0

W zasadzie to po prostu się uczę o tym keywordzie i skądś się dowiedziałem że większość składowych w klasach bez używania this powinny być static.

0

Inicjalizacja zmiennej statycznej i "wywołanie" do tego celu funkcji loadProvincesFromFile() następuje w momencie uruchomienia programu przed "wywołaniem" pierwszej instrukcji z int main{...}.
Jeżeli chciałbyś obsłużyć wyjątek wyrzucony z tej funkcji, to musiałaby ona zostać wywołana w jakimś bloku try{...} co jest w tym przypadku niemożliwe.

Plik nie musi być tylko uszkodzony, aplikacja może na przykład ładować go z serwera z Australii więc musi obsłużyć przypadek w którym szwankuje połączenie internetowe.

0

To w takim wypadku widzę dwa rozwiązania: zrezygnowanie ze static i zaoranie klasy albo stworzenie oddzielnej na ładowanie danych. Ewentualnie nie ładować z pliku?

0
Cyberah napisał(a):

Game to klasa i składowa provinces jest prywatna. Nie wiem co ja mam z tym globalnym zasięgiem, po prostu źle mi się kojarzy i myślałem że takie definicje czy inne rzeczy powinny być jakoś spakowane w funkcji czy w innym, lokalnym zasięgu.

To NIE jest globalny zasięg. Być może źle kojarzy ci się ze składnią jakiegoś innego języka, nie wiem, ale takie coś:

std::vector<std::string> Game::provinces = ...

wyraźnie wskazuje, że provinces jest elementem składowym Game. Jakby miało być globalne, to by było samo provinces, albo ::provinces, a jest Game::provinces.

0

Odpowiadając na pytanie - można.

main.cpp

#include <iostream>
#include "game.h"

using namespace std;

int main()
{
    cout << "Test wczytania prowincji\n";
    
    Game::loadProvincesFromFile("wrzutka 1 do statycznego kontenera");
    Game::loadProvincesFromFile("kolejna wrzutka do statycznego kontenera");
    
    for (auto &province : Game::provinces)
    {
        cout << province << "\n";
    }
    
    return 0;
}

game.h

#ifndef GAME_H
#define GAME_H

#include <vector>
#include <string>

class Game
{
public:
  static void loadProvincesFromFile(const std::string &appendToStaticContainer);
   
  static std::vector<std::string> provinces;
};

#endif

i game.cpp

#include "game.h"

std::vector<std::string> Game::provinces = {"poo", "foo"}; //no i to działa. Logiczne.

void Game::loadProvincesFromFile(const std::string &appendToStaticContainer) 
{
    //std::vector<std::string> Game::provinces; //cannot be defined in the current scope
    provinces.push_back("duu");
    provinces.push_back("paa");
    
    static std::vector<std::string> container;
    container.push_back(appendToStaticContainer);
    
    std::cout << "Listowanie zawartości statycznego wektora" << "\n";
    for (auto &it : container)
    {
        std::cout << it << "\n";
    }
}

https://onlinegdb.com/SktweUigS

1 użytkowników online, w tym zalogowanych: 0, gości: 1