5
\$\begingroup\$

I created a class to add functionality to c++ std::string (std::basic_string actually), let's call it String.

I would like to know, what changes to do to String to boost it performance. Maybe having an internal std::basic_string member is not the best idea and it would be better to implement my own (that maybe would be re-inventing the wheel) or maybe the class itself is just not necessary and would be better just to write down functions under a namespace.

That being said, here is the code:

String.h

#pragma once

#include <string>

template <class CharType = wchar_t>
class String {
public:
  String();
  String(const CharType*);
  String(std::basic_string<CharType>);
  ~String();

  std::size_t length();
  std::size_t size();
  std::size_t bytesize();

  std::string to_bytes();

  static std::basic_string<CharType> from_bytes(const char*);
  static std::basic_string<CharType> from_bytes(std::string);

  std::basic_string<CharType> std;
};

String.cpp

#include "String.h"

#include <iostream>
#include <string>

#include <locale>
#include <codecvt>

//  Default constructor with no parameters
template <class CharType>
String<CharType>::String() { }

//  Constructor for:
//    String<char> variable = "Hi there";
//    String<char16_t> variable = u"Hi there";
template <class CharType>
String<CharType>::String(const CharType* str): std(str) { }

//  Normal Constructor
//    String<char> variable("Hi there");
//    String<char16_t> variable(u"Hi there");
template <class CharType>
String<CharType>::String(std::basic_string<CharType> str): std(str) { }

//  Destructor
template <class CharType>
String<CharType>::~String() { }

//  Get the length of a string, it's not necessary to calculate it
//  since std::string already provides this functionality
template <class CharType>
std::size_t String<CharType>::length() {
  return std.length();
}

//  Now, in a string of `char` the length would be different of expected
//  as actually it counts special characters by its number of bytesize
//  with this functionality it counts those characters as one
//  Demonstration
//    std::char_traits<char>::length("Ni hao 你好")
//      ^ 13 since each chinese character counts as 3
//
//  String<char>("Ni hao 你好").length();
//      ^ 9 as expected
//
//  Default ::length() functionality can be accesed through `std` member
//  Example:
//    String<char>("Ni hao 你好").std.length();
//      ^ 13
template <>
std::size_t String<char>::length() {
  return String<char32_t>::from_bytes(std.c_str()).length();
}

//  Use already implemented functinality from std::string
template <class CharType>
std::size_t String<CharType>::size() {
  return std.size();
}

//  Convert string special character to bytes and count
template <class CharType>
std::size_t String<CharType>::bytesize() {
  return to_bytes().size();
}

//  Convert the actual string into byte representation
template <class CharType>
std::string String<CharType>::to_bytes() {
  std::wstring_convert<std::codecvt_utf8_utf16<CharType>, CharType> converter;

  return converter.to_bytes(std);
}

//  `char` doesn't need to convert, so return itself
template <> std::string String<char>::to_bytes() {
  return std;
}

//  Create a new wide characters string using a bytes string
template <class CharType>
std::basic_string<CharType> String<CharType>::from_bytes(const char* str) {
  std::wstring_convert<std::codecvt_utf8<CharType>, CharType> converter;

  return converter.from_bytes(str);
}

template <>
std::basic_string<char16_t> String<char16_t>::from_bytes(const char* str) {
  std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;

  return converter.from_bytes(str);
}

//  When creating from bytes inside of a std::string
template <class CharType>
std::basic_string<CharType> String<CharType>::from_bytes(std::string str) {
  return String<CharType>::from_bytes(str.data());
}

//  Needs to be implemented to prevent errors between `std::codecvt` and `char`
template <>
std::basic_string<char> String<char>::from_bytes(const char* str) {
  return str;
}

//  Why would anyone include a .cpp file
template class String<char>;
template class String<wchar_t>;
template class String<char16_t>;
template class String<char32_t>;

Example of usage:

const char* text = "Hi there \u270c\u270c";

String<wchar_t> test_wchar = String<wchar_t>::from_bytes(text);
test_wchar.bytesize();
test_wchar.length();
test_wchar.std // this member is std::basic_string<wchar_t>
\$\endgroup\$
  • \$\begingroup\$ You can use folly::fbstring for internal strings if performance matters to you. They also have utility functions. \$\endgroup\$ – coyotte508 Jun 9 '16 at 13:45
  • 2
    \$\begingroup\$ I don't understand why you would write this class rather than derive a class from std::wstring and add your extra functions in there. \$\endgroup\$ – Code Gorilla Jun 9 '16 at 13:54
  • \$\begingroup\$ @coyotte508 Looks pretty good, I like working with projects made by the big ones (like Skia). Not really my coding style but I will try it out! \$\endgroup\$ – Alvaro Carneiro Jun 9 '16 at 13:54
  • \$\begingroup\$ I don't quite see the point of this whole exercise. I recommend to simply use std::wstring, but provide overloaded (non-member) functions that compute the actual_length(std::basic_string<T>) etc. That way, you maintain the full advantage of all functionality provided by the std library (iterators etc) and only explicitly add the functionality you actually want. \$\endgroup\$ – Walter Jun 11 '16 at 16:00
2
\$\begingroup\$

Shouldn't these declarations without definitions be sufficient?

String() = default;
~String() = default;

I would personally skip such not-strictly-necessary declarations completely but that is probably just a subjective opinion.

Would you like to enable using String as a base class? If so you might probably need to consider having virtual destructor.

Some methods could be const:

std::size_t length() const;
std::size_t size() const;
std::size_t bytesize() const;
std::string to_bytes() const;

I guess you know that calling std::string(const char*) ctor with nullptr is undefined. It might be tricky to spot it behind your interface:

std::basic_string<char> String<char>::from_bytes(const char* str) {
    return str;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.