0

I'm having trouble specializing 2 methods of a tokenizer class that's declared with 2 template parameters. I've referenced Template specialization of a single method from a templated class but I am still encountering a few errors with my implementation. Some code (specialized functions near EOF):

#pragma once
#include "stdafx.h"
#include <string>
#include <vector>
#include <sstream>
#include <stdexcept>

inline const std::string emptyString = "";
inline const std::wstring emptyWString = L"";

template <class stringT = std::string, class delimiterT = char>
class Tokenizer
{
private:
    std::vector<stringT> tokens;

    bool enableParserThrow;

public:
    Tokenizer(bool throwOnParseError) : enableParserThrow(throwOnParseError)
    {
    }

    Tokenizer(const stringT& tokenizeMe, delimiterT delimiter) : Tokenizer(true)
    {
        TokenizeString(tokenizeMe, delimiter);
    }

    void TokenizeString(const stringT& str, delimiterT delimiter)
    {
        std::stringstream ss;
        ss << str;

        std::string token;
        while (std::getline(ss, token, delimiter))
        {
            tokens.push_back(token);
        }
    }

    template <class T>
    T ParseToken(size_t tokenIndex)
    {
        if (tokenIndex < 0 || tokenIndex >= tokens.size())
        {
            ThrowParserExceptionIfEnabled("Index out of range.");
            return T();
        }

        T temp;
        std::stringstream ss;
        ss << tokens[tokenIndex];
        ss >> temp;

        if (ss.fail())
            ThrowParserExceptionIfEnabled("Parse failure.");

        return temp;
    }

    void Clear()
    {
        tokens.clear();
    }

    const std::vector<stringT>& GetTokens()
    {
        return tokens;
    }

    void ThrowParserExceptionIfEnabled(const char* message)
    {
        if (enableParserThrow)
        {
            throw std::runtime_exception(message);
        }
    }

    // Trying to specialize these functions so I can return a reference to a global empty std::string or std::wstring if tokeIndex is out of range
    template<>
    const std::string& Tokenizer<std::string, delimiterT>::operator[](size_t tokenIndex);

    //TODO:
    //template<>
    //const std::string& Tokenizer<std::wstring, delimiterT>::operator[](size_t tokenIndex);
};

template<class stringT, class delimiterT>
inline const std::string & Tokenizer<stringT, delimiterT>::operator[](size_t tokenIndex)
{
    return emptyString;
}

What is the proper specialization definition of Tokenizer<>::operator[]?

I'm receiving the following errors with this implementation: Errors

kiner_shah
  • 3,939
  • 7
  • 23
  • 37
  • Please share the error messages you get as well. – cigien Dec 20 '21 at 03:37
  • Seems my question was closed thinking linked question https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file answers this post. Unfortunately, it does not. – user13873040 Dec 20 '21 at 04:09
  • As mentioned, you need to add the error messages you're getting. – cigien Dec 20 '21 at 04:20
  • Sorry about that, I've updated my implementation inlining the specialized methods directly in the header file. I've also included a screenshot of the errors I'm running into. – user13873040 Dec 20 '21 at 04:35
  • 2
    Error messages are text. Please do not post links to images of text information. Copy and paste text as text. – n. m. could be an AI Dec 20 '21 at 05:55
  • Your `operator[]` is not a function template to begin with. It is a non-template member if a class template. You cannot specialise individual members of class templates. (2) Why do you need this specialisation anyway? And why do you need delimiterT? – n. m. could be an AI Dec 20 '21 at 06:05
  • The specialization was needed in order to return a global (and empty) std::wstring or std::string depending on the stringT's type. delimiterT is needed to make std::getline happy in the future. With the current way TokenizeString() is implemented, it's probably unnecessary. – user13873040 Dec 28 '21 at 01:50

1 Answers1

0

As your operator[] is not a template, you can't specialize it inside your class. What you want is to specialize the non template method for a template class.

But you can't do a partial specialization for the class to add definitions of members to it.

Working example:

template <class stringT = std::string, class delimiterT = char>
class Tokenizer
{
    const std::string& operator[](size_t tokenIndex);
};

template<>
inline const std::string& Tokenizer<std::wstring, char>::operator[](size_t )
{
    return emptyString;
}
template< >
inline const std::string& Tokenizer<std::string, char>::operator[](size_t )
{
    return emptyString;
}

To get rid of that problem you should check if your method really depends on all template parameters. If not, you can simply put them to a base class, which only depends on a single template parm and inherit from there. Maybe other code organization may also help.

Klaus
  • 24,205
  • 7
  • 58
  • 113