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

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
2

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: ```cpp template <class t="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.
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).

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ąć.

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.

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;

}

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