Search code examples
c++iso-8859-1stdstring

Implementing basic_string<unsigned char> for ISO 8859-1


Background:

I'm a C programmer, and I've just decided to play around with C++.

Objective:

Create a class to read ISO 8859-1 characters as a string using std::basic_string. I know I could just use a mapping function within std::string, but I wanted to try this way for learning reasons.

Problem:

I've created a class that extends from char_traits and a class that implements basic_string. Right now I'm trying to create the constructor. The constructor should accept a const char pointer and allocate space for it.

Based on this, this constructor already exists:

basic_string (const charT* s, const allocator_type& alloc = allocator_type());

And it's defined as:

from c-string

Copies the null-terminated character sequence (C-string) pointed by s.
The length is determined by calling traits_type::length(s)."

So I assumed I could just reuse that constructor, passing the correct arguments (in this case, unsigned char instead of char), but either I don't know how to use default parameters properly, or the constructor doesn't exist.

I'm not sure if this is the correct approach, so any tips/hint on how to do this are welcome.

Error:

test.cpp: In constructor ‘ISO_8859_1_String::ISO_8859_1_String(const char*)’:
test.cpp:18:72: error: no matching function for call to ‘ISO_8859_1_String::ISO_8859_1_String(const unsigned char*, NULL)’
test.cpp:18:72: note: candidates are:
test.cpp:16:5: note: ISO_8859_1_String::ISO_8859_1_String(const char*)
test.cpp:16:5: note:   candidate expects 1 argument, 2 provided
test.cpp:14:7: note: ISO_8859_1_String::ISO_8859_1_String(const ISO_8859_1_String&)
test.cpp:14:7: note:   candidate expects 1 argument, 2 provided

Code:

#include <iostream>

using namespace std;

class ISO_8859_1_Char_Traits : public char_traits<unsigned char>{
  public: 
    // Simple length implementation
    static size_t length (const unsigned char* s){
      size_t i = 0;
      while (s[i++] != 0x00){};
      return i;
    }
};

class ISO_8859_1_String : public basic_string<unsigned char, ISO_8859_1_Char_Traits, allocator<unsigned char> >{
  public:
    ISO_8859_1_String(const char* s){
      ISO_8859_1_String(reinterpret_cast<const unsigned char*>(s), NULL);
   }
};

int main(){
  ISO_8859_1_String* test = new ISO_8859_1_String("test");
  
  return 1;
}

Solution

  • In this:

    class ISO_8859_1_String : public basic_string<unsigned char, ISO_8859_1_Char_Traits, allocator<unsigned char> >{
      public:
        ISO_8859_1_String(const char* s){
          ISO_8859_1_String(reinterpret_cast<const unsigned char*>(s), NULL);
       }
    };
    

    Either my eyes are tricking me, or you are calling the constructor (in the constructor itself, as it turns out) with the wrong number of arguments. Also, even if you passed the right number of arguments, you'd recurse infinitely.

    EDIT: Also, the types of the first argument don't match anyway.

    EDIT2: Okay, I think I know what you are trying to do. You are trying to pass arguments to the constructor of your base class. For that, you need different syntax:

        ISO_8859_1_String(const char* s)
         : basic_string<unsigned char, ISO_8859_1_Char_Traits, allocator<unsigned char> >(reinterpret_cast<const unsigned char*>(s), NULL) {
       }
    

    Also, I'd use some type aliases or typedefs to make that more readable.

    ADDENDUM: Base class ctor arguments must be passed via the initialization list, not in the body of the derived class ctor:

    class A {
        A() {}
        A(int) {}
    };
    
    class B1 : public A{
        // This means: Call the base class ctor with a 1.
        B1() : A(1) {]
        // This means: Call the base class ctor with no arguments.  Then create a
        // temporary A object, passing 1 to the ctor, and then throw it away.
        // B1() { A(1); }
    };