Użycie statycznej funkcji szablonowej przez funkcję w tej samej klasie

Odpowiedz Nowy wątek
2015-09-22 19:27
0

Witam.
Dlaczego przy próbie użycia statycznej funkcji szablonowej przez statyczną funkcję w tej samej klasie otrzymuję błędy przy próbie kompilacji?

Globals.h

class Globals
{
public:
    static int GetDaysBetweenTwoDates(const std::string& firstDate, std::string secondDate = "");

    template <class T> static std::vector<T> Split_By_Chars(const std::string &Line, const std::string& Split, int Base = 10, int Max_Size = 0, int Skip = 0, int Start_Index = 0);
};

Globals.cpp

#include "Globals.h"
int Globals::GetDaysBetweenTwoDates(const std::string& firstDate, std::string secondDate)
{
//  std::string c = "T|T";
//  auto values = Globals::Split_By_Chars<int>(c, std::string("-"));
}

template <> std::vector<int> Globals::Split_By_Chars(const std::string &Line, const std::string& Split, int Base, int Max_Size, int Skip, int Start_Index)
{
    std::vector<int> Result;
    std::string Rest;

    std::vector<std::string> Splited_To_String = Split_By_Chars_To_String(Line, { Split }, Max_Size, Skip, Start_Index);

    for (auto &TB_01 : Splited_To_String) Result.push_back(strtol(TB_01.c_str(), NULL, Base));

    return Result;
}
template <> std::vector<double> Globals::Split_By_Chars(const std::string &Line, const std::string& Split, int Base, int Max_Size, int Skip, int Start_Index)
{
    std::vector<double> Result;
    std::string Rest;

    std::vector<std::string> Splited_To_String = Split_By_Chars_To_String(Line, { Split }, Max_Size, Skip, Start_Index);

    for (auto &TB_01 : Splited_To_String) Result.push_back(std::stod(TB_01));

    return Result;
}

main.cpp

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE P, LPSTR CMD, int nShowCmd)
{
    std::string c = "T|T";
    auto values = Globals::Split_By_Chars<int>(c, std::string("-"));        
}

Powyższy kod kompiluję się oraz działa bez żadnych problemów, jednak po odkomentowaniu zawartości funkcji Globals::GetDaysBetweenTwoDates przy próbie kompilacji pojawiają się poniższe błędy:

Error   C2910   'Globals::Split_By_Chars': cannot be explicitly specialized
Error   C2908   explicit specialization; 'std::vector<int,std::allocator<int>> Globals::Split_By_Chars<int>(const std::string &,const std::string &,int,int,int,int)' has already been instantiated

Pozostało 580 znaków

2015-09-22 19:34

Po co ci szablon, skoro jego treści nie ma w nagłówku, a w cpp każdą wersję definiujesz ręcznie? To jest zaprzeczenie użycia szablonów!

Nazwa klasy Globals oznacza, że robisz coś źle.

A problem polega na tym, że definicji szablonu nie ma w nagłówku, więc w main.cpp kompilator nie wie, jak wygląda ten szablon i ni jest w stanie się domyśleć, że definiujesz go w innym cpp.
Pomijając fakt durnowatości tego szablonu, powinieneś zadeklarować specjalizację tego szablonu.

class Globals
{
public:
    static int GetDaysBetweenTwoDates(const std::string& firstDate, std::string secondDate = "");

    template <class T> static std::vector<T> Split_By_Chars(const std::string &Line, const std::string& Split, int Base = 10, int Max_Size = 0, int Skip = 0, int Start_Index = 0);

    template <> static std::vector<int> Split_By_Chars(const std::string &Line, const std::string& Split, int Base = 10, int Max_Size = 0, int Skip = 0, int Start_Index = 0);
    template <> static std::vector<double> Split_By_Chars(const std::string &Line, const std::string& Split, int Base = 10, int Max_Size = 0, int Skip = 0, int Start_Index = 0);
};

po dokładniejszej analizie co autor miał na myśli:

template <class T> 
T Parse(const string &s, int base = 10) {
     istringstream stream(s);
     T result();
     stream >> result;
     return result;
}

template <> 
int Parse(const string &s, int base = 10) {
     return strtol(s.c_str(), NULL, Base);
}

template <class T> 
std::vector<T> Split_By_Chars(const std::string &Line, 
         const std::string& Split, 
         int Base,
         int Max_Size,
         int Skip,
         int Start_Index)
{
    std::vector<T> Result;
    std::string Rest;

   auto Splited_To_String = Split_By_Chars_To_String(Line, { Split }, Max_Size, Skip, Start_Index);

    for (auto &TB_01 : Splited_To_String) {
       Result.push_back(Parse<T>(TB_01, base));
    }
    return Result;
}

Powiem szczerze, że strasznie koślawy ten kod, może opisz co chcesz uzyskać to ktoś ci to napisze tak jak należy.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 4x, ostatnio: MarekR22, 2015-09-22 19:58

Pozostało 580 znaków

2015-09-22 20:44
0

Dziękuję za pomoc, zmieniłem kod według Twojego, dzięki czemu wygląda zdecydowanie lepiej.

Celem funkcji jest zamiana tekstu na tablicę (vector) liczb, Split_By_Chars_To_String zwraca std::vector zawierający poszczególne wyrazy (liczby).

Pozostało 580 znaków

2015-09-22 20:54
0

tyle to ja zrozumiałem. Pytanie jest co parsujesz i czemu potrzebujesz takie argumenty?
Domyślam się, że:

  • Line to napis wejściowy, który parsujesz
  • Split to pewnie lista znaków, które traktujesz jako separator (kolejność ważna? czy długość tego napisu określa długoś vertora wynikowego)
  • reszta jest niezrozumiała

Najlepiej wypisz kilka przykładowych stringów do sparsowania i co chcesz z nich wyciągnąć.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 2x, ostatnio: MarekR22, 2015-09-22 20:54

Pozostało 580 znaków

2015-09-22 20:56
0

static int GetDaysBetweenTwoDates(const std::string& firstDate, std::string secondDate = "");

wut?

powinieneś mieć klasę Date, powiedzmy, jej metodę Parse przyjmującą stringa a zwracającą Date, albo konstruktor przyjmujący stringa, a funkcja GetDaysBetween powinna operować na zmiennych tej klasy, a nie na stringach.

edytowany 1x, ostatnio: Azarien, 2015-09-22 20:57
2015-09-22 21:07
0

@MarekR22

  • Line - napis wejściowy
  • Split - separator, separatorem jest cały string, a nie jego pojedyncze znaki, Split_By_Chars_To_String przyjmuje std::vector<std::string>> zawierający listę separatorów, jednak z racji że żadne wywołanie tej funkcji nie korzysta z tej kilku separatorów, to przyjmuje zawsze dokładnie jeden
  • Max_Size - Maksymalny rozmiar wynikowego vectora, w przypadku gdy nie jest potrzebna dalsza zawartość linii funkcja zwraca tylko jej część
  • Skip - Ilość słów do pominięcia, w przypadku gdy pierwsze wyrazy zawierają niepotrzebne dane mogą zostać pominięte
  • Start_Index - Numer pierwszego znaku, w przypadku gdy dane zawierają niepotrzebne dane na początku o stałej długości mogą zostać pominięte

Przykładowe dane:
15|78|95 - 15, 78, 95
2015-09-07 - 2015,09,07
text: 56|78|98 - text, 56|78|98

template <class T> std::vector<T> Globals::Split_By_Chars_To_String(const T &Line, const std::vector<T>& Split, int Max_Size, int Skip, int Start_Index)
{
    std::vector<T> Result;
    T Rest;

    for (size_t TB_01 = Start_Index; TB_01 < Line.size(); ++TB_01)
    {
        bool Split_Now = true;

        for (auto &TB_02 : Split)
        {
            if (TB_01 + TB_02.size() > Line.size())
            {
                Split_Now = false;
                continue;
            }

            for (size_t TB_03 = 0; TB_03 < TB_02.size(); ++TB_03)
            {
                if (Line[TB_01 + TB_03] != TB_02[TB_03])
                {
                    Split_Now = false;
                    break;
                }
            }

            if (Split_Now)
            {
                TB_01 += TB_02.size() - 1;
                break;
            }
        }

        if (Split_Now)
        {   
            if (Skip <= 0) Result.push_back(Rest);
            else --Skip;

            Rest.clear();

            if (Result.size() == Max_Size) return Result;
        }
        else
        {
            Rest += Line[TB_01];
        }
    }

    if (!Rest.empty())
    {   
        if (Skip <= 0) Result.push_back(Rest);
    }

    return Result;
}

@Azarien
Datę przechowuję jako std::string, jest ona potrzebna do dodania jej do nazwy pliku, odczytu liczby dni pomiędzy dwoma datami oraz pobrania aktualnej godziny, kod do jej pobrania znalazłem dosyć dawno i aby uniknąć zabawy z time_t zamienią ją od razu na std::string.

Globals zawiera funkcje pochodzące z poprzednich projektów, obecnie staram się dzielić kod na poszczególne klasy, gdy rozszerzam pewną funkcjonalność zawartą w tej klasie w stopniu znaczącym to wydzielam ją do osobnej klasy, jednak w przypadku jednej lub dwóch funkcji zostawiam ją Globals

std::string Globals::CurrentDate_FileName(){

    time_t rawtime;
    struct tm timeinfo;

    time ( &rawtime );
    localtime_s(&timeinfo, &rawtime);

    std::string Month = std::to_string(timeinfo.tm_mon + 1);
    std::string Day = std::to_string(timeinfo.tm_mday);

    if (Month.size() == 1) Month = "0" + Month;
    if (Day.size() == 1) Day = "0" + Day;

    return std::to_string(timeinfo.tm_year + 1900) + "-" + Month + "-" + Day;

}
edytowany 3x, ostatnio: Kamil9132, 2015-09-22 21:20
Do dzielnia łańcucha użyj std::getline. Do formatowania czasu masz std::put_time. Do liczenia dni jest np. std::chrono. :-o - Endrju 2015-09-22 21:25

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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