std::basic_string template specialization - c++

I'm trying to create my own string class using std::basic_string<> with a custom allocator. It's seems to be mostly working thanks in large part to other related topics on stackoverflow. But I really want this to be a drop in replacement of std::string. To that end, it needs to assignable to/from std:string. I tried overloading assignment operators and such but I can't seem to get that working.
The end goal here is to be able to log/track memory allocations for strings in existing code by replacing std::string. If there's a better way of doing that, I'd like to hear it as well.
This is an embedded application so my options with 3rd party tools is limited.
Here's a sample app with the code I have already.
dave
#include <iostream>
using namespace std;
namespace mw_allocator_namespace
{
int mw_allocator_space =0;
template <typename T>
class mw_allocator: public std::allocator<T>
{
public:
typedef size_t size_type;
typedef T* pointer;
typedef const T* const_pointer;
template<typename _Tp1>
struct rebind
{
typedef mw_allocator<_Tp1> other;
};
pointer allocate(size_type n, const void *hint=0)
{
pointer p = std::allocator<T>::allocate(n, hint);
mw_allocator_space += n*sizeof(T);
cout << hex << " Alloc: " << n << " : " << n*sizeof(T) << " (" << reinterpret_cast<unsigned long long>(p) << ")" << endl;
return p;
}
void deallocate(pointer p, size_type n)
{
cout << hex << " Dealloc: " << n << " : " << n*sizeof(T) << " (" << reinterpret_cast<unsigned long long>(p) << ")" << endl;
mw_allocator_space -= n*sizeof(T);
std::allocator<T>::deallocate(p, n);
}
mw_allocator() throw(): std::allocator<T>() { cout << " Hello allocator!" << endl; }
mw_allocator(const mw_allocator &a) throw(): std::allocator<T>(a) { }
template <class U>
mw_allocator(const mw_allocator<U> &a) throw(): std::allocator<T>(a) { }
~mw_allocator() throw() { }
};
}
typedef std::basic_string<char, std::char_traits<char>, mw_allocator_namespace::mw_allocator<char> > my_string;
int main() {
string s1("Hello World1");
my_string ms1;
#if 0
// This doesn't work
ms1 = s1;
#else
// This does...
ms1 = s1.c_str();
#endif
cout << "ms1: " << ms1 << endl;
return 0;
}

Related

How to deduce template by struct's member

Suppose I have some structs and each of them holds one enum as a member.
I want to call a method of a struct but depending on a struct's member, like in the code example:
#include <iostream>
#include <string>
#include <type_traits>
enum class Type{
lowercase = 0,
uppercase
};
struct Low{
static constexpr Type cp = Type::lowercase;
};
struct Up{
static constexpr Type cp = Type::uppercase;
};
template<typename Case, typename=void>
struct Printer
{
void print_g(const std::string& s){
std::cout << "just s: " << s << std::endl;
}
};
template<typename X>
struct Printer<X, std::enable_if_t<X::cp == Type::lowercase, void>>
{
void print_g(const std::string& s){
std::cout << "lowercase " << std::nouppercase << s << std::endl;
}
};
template<typename X>
struct Printer <X, std::enable_if_t<X::cp == Type::uppercase, void>>
{
void print_g(const std::string& s){
std::cout << "uppercase " << std::uppercase << s << std::endl;
}
};
int main()
{
Printer<Low> pl;
pl.print_g("hello1");
Printer<Up> p2;
p2.print_g("hello2");
}
But this solution doesn't look quite elegant to me.
Especially the part typname=void in the first template.
Only then code compiles. Why is that the case?
And is there any better (more elegant) solution for this template specialization?
In C++17, you can use if constexpr:
template <typename X>
struct Printer
{
void print_g(const std::string& s)
{
if constexpr(X::cp == Type::lowercase)
{
std::cout << "lowercase " << std::nouppercase << s << std::endl;
}
else if constexpr(X::cp == Type::uppercase)
{
std::cout << "uppercase " << std::uppercase << s << std::endl;
}
else
{
std::cout << "just s: " << s << std::endl;
}
}
};
If you do not have access to C++17, consider these options:
Use a regular if...else statement. There's no code that needs to be conditionally compiled in your example.
Implement static_if in C++14. Here's a talk I gave that explains how to do it: Implementing static control flow in C++14
You can fully specialize Printer for Low and Up.
template<class Case>
struct Printer
{
void print_g(const std::string& s) {
std::cout << "just s: " << s << std::endl;
}
};
template<>
struct Printer<Low>
{
void print_g(const std::string& s) {
std::cout << "lowercase " << std::nouppercase << s << std::endl;
}
};
template<>
struct Printer<Up>
{
void print_g(const std::string& s) {
std::cout << "uppercase " << std::uppercase << s << std::endl;
}
};
Notice that the enum does not come into play at all. If you need to specialize for the enum, you can do that too.
template<Type Case>
struct PrinterHelper
{
void print_g(const std::string& s) {
std::cout << "just s: " << s << std::endl;
}
};
template<>
struct PrinterHelper<Type::lowercase>
{
void print_g(const std::string& s) {
std::cout << "lowercase " << std::nouppercase << s << std::endl;
}
};
template<>
struct PrinterHelper<Type::uppercase>
{
void print_g(const std::string& s) {
std::cout << "uppercase " << std::uppercase << s << std::endl;
}
};
template<class Case>
using Printer = PrinterHelper<Case::cp>;
I would likely just go with:
enum struct Casing
{
Lower,
Upper
};
template<Casing>
struct printer;
template<>
struct printer<Casing::Lower>
{
...
};
template<>
struct printer<Casing::Upper>
{
...
};

How change class of a C++ object (implementing a variadic type)

First off: I know that it is generally a bad idea to change an object's class, but I'm implementing my own programming language, and it has variables that can contain values of any type, and even change their type at will, so please assume I'm not a beginner not understanding OO basics.
Currently, I implement my variant variables in C. Each one has a pointer to a table of function pointers, containing functions like SetAsInt(), SetAsString() etc., followed by what would be instance variables in C++. All objects are the same size.
When a variable contains a string and someone assigns an Int to it, I manually call the destructor, change the table of function pointers to point to the table used for variadic int values, and then set its int instance variable.
This is a bit hard to maintain, as every time I add a new type, I have to add a new table of function pointers and fill out all the function pointers in it. Structs of function pointers seem to be very badly type-checked, and missing fields don't lead to complaints, so I can easily accidentally forget one pointer in the list and get interesting crashes. Also, I have to repeat all the function pointers that are the same in most types.
I'd like to implement my variadic types in C++ instead, where a lot of this type-checking and inheriting default behaviours is done for me by the compiler. Is there a safe way to do this?
PS - I know I could create a wrapper object and use new to allocate a new object, but I can't have the additional extra allocation overhead for every int variable on the stack.
PPS - The code needs to be portable across Linux, Mac, iOS and Windows for now, but if someone has a standard C++ solution, that would be even better.
PPPS - The list of types is extensible, but predetermined at compile-time. The base layer of my language defines just the basic types, but the host application my language is compiled into adds a few more types.
Usage Example:
CppVariant someNum(42); // Creates it as CppVariantInt.
cout << "Original int: " << someNum->GetAsInt()
<< " (" << someNum->GetAsDouble() << ")" << endl;
someNum->SetAsInt(700); // This is just a setter call.
cout << "Changed int: " << someNum->GetAsInt()
<< " (" << someNum->GetAsDouble() << ")" << endl;
someNum->SetAsDouble(12.34); // This calls destructor on CppVariantInt and constructor on CppVariantDouble(12.34).
cout << "Converted to Double: " << someNum->GetAsInt()
<< " (" << someNum->GetAsDouble() << ")" << endl; // GetAsInt() on a CppVariantDouble() rounds, or whatever.
(Imagine that beyond double and int, there would be other types in the future, like strings or booleans, but the caller of GetAsInt()/SetAsInt() shouldn't have to know what it is stored as, as long as it can be converted at runtime)
Here is a solution based on type-erasure, union and template specializations.
I'm not sure it fits your requirements.
Anyway, here is what it gets:
Anything is placed on the dynamic storage
No hierarchy required
You can easily improve it further to reduce the amount of code, but this aims to serve as a base point from which to start.
It follows a minimal, working example based on the intended use in the question:
#include<iostream>
class CppVariant {
union var {
var(): i{0} {}
int i;
double d;
};
using AsIntF = int(*)(var);
using AsDoubleF = double(*)(var);
template<typename From, typename To>
static To protoAs(var);
public:
CppVariant(int);
CppVariant(double);
int getAsInt();
double getAsDouble();
void setAsInt(int);
void setAsDouble(double);
private:
var data;
AsIntF asInt;
AsDoubleF asDouble;
};
template<>
int CppVariant::protoAs<int, int>(var data) {
return data.i;
}
template<>
int CppVariant::protoAs<double, int>(var data) {
return int(data.d);
}
template<>
double CppVariant::protoAs<int, double>(var data) {
return double(data.i);
}
template<>
double CppVariant::protoAs<double, double>(var data) {
return data.d;
}
CppVariant::CppVariant(int i)
: data{},
asInt{&protoAs<int, int>},
asDouble{&protoAs<int, double>}
{ data.i = i; }
CppVariant::CppVariant(double d)
: data{},
asInt{&protoAs<double, int>},
asDouble{&protoAs<double, double>}
{ data.d = d; }
int CppVariant::getAsInt() { return asInt(data); }
double CppVariant::getAsDouble() { return asDouble(data); }
void CppVariant::setAsInt(int i) {
data.i = i;
asInt = &protoAs<int, int>;
asDouble = &protoAs<int, double>;
}
void CppVariant::setAsDouble(double d) {
data.d = d;
asInt = &protoAs<double, int>;
asDouble = &protoAs<double, double>;
}
int main() {
CppVariant someNum(42);
std::cout << "Original int: " << someNum.getAsInt() << " (" << someNum.getAsDouble() << ")" << std::endl;
someNum.setAsInt(700);
std::cout << "Changed int: " << someNum.getAsInt() << " (" << someNum.getAsDouble() << ")" << std::endl;
someNum.setAsDouble(12.34);
std::cout << "Converted to Double: " << someNum.getAsInt() << " (" << someNum.getAsDouble() << ")" << std::endl;
}
On a lark, I tried using placement new to do this, and I have ... something ... It compiles, it does the job, but I'm not sure if it's an improvement over pure C. Since I can't have a union of C++ objects, I create a CPPVMAX() macro to pass the largest sizeof() of all subclasses as the size to mBuf[], but that's not really pretty either.
#include <iostream>
#include <string>
#include <cmath>
#define CPPVMAX2(a,b) (((a) > (b)) ? (a) : (b))
#define CPPVMAX3(a,b,c) CPPVMAX2((a),CPPVMAX2((b),(c)))
using namespace std;
class CppVariantBase
{
public:
CppVariantBase() { cout << "CppVariantBase constructor." << endl; }
virtual ~CppVariantBase() { cout << "CppVariantBase destructor." << endl; }
virtual int GetAsInt() = 0;
virtual double GetAsDouble() = 0;
virtual void SetAsInt( int n );
virtual void SetAsDouble( double n );
};
class CppVariantInt : public CppVariantBase
{
public:
CppVariantInt( int n = 0 ) : mInt(n)
{
cout << "CppVariantInt constructor." << endl;
}
~CppVariantInt() { cout << "CppVariantInt destructor." << endl; }
virtual int GetAsInt() { return mInt; }
virtual double GetAsDouble() { return mInt; }
virtual void SetAsInt( int n ) { mInt = n; }
protected:
int mInt;
};
class CppVariantDouble : public CppVariantBase
{
public:
CppVariantDouble( double n = 0 ) : mDouble(n)
{
cout << "CppVariantDouble constructor." << endl;
}
~CppVariantDouble()
{
cout << "CppVariantDouble destructor." << endl;
}
virtual int GetAsInt()
{
if( int(mDouble) == mDouble )
return mDouble;
else
return round(mDouble);
}
virtual double GetAsDouble() { return mDouble; }
virtual void SetAsDouble( int n ) { mDouble = n; }
protected:
double mDouble;
};
class CppVariant
{
public:
CppVariant( int n = 0 ) { new (mBuf) CppVariantInt(n); }
~CppVariant() { ((CppVariantBase*)mBuf)->~CppVariantBase(); }
operator CppVariantBase* () { return (CppVariantBase*)mBuf; }
CppVariantBase* operator -> () { return (CppVariantBase*)mBuf; }
protected:
uint8_t mBuf[CPPVMAX3(sizeof(CppVariantBase),sizeof(CppVariantInt),sizeof(CppVariantDouble))];
};
void CppVariantBase::SetAsInt( int n )
{
this->~CppVariantBase();
new (this) CppVariantInt(n);
}
void CppVariantBase::SetAsDouble( double n )
{
this->~CppVariantBase();
new (this) CppVariantDouble(n);
}
int main(int argc, const char * argv[]) {
CppVariant someNum(42);
cout << "Original int: " << someNum->GetAsInt()
<< " (" << someNum->GetAsDouble() << ")" << endl;
someNum->SetAsInt(700); // This is just a setter call.
cout << "Changed int: " << someNum->GetAsInt()
<< " (" << someNum->GetAsDouble() << ")" << endl;
someNum->SetAsDouble(12.34); // This changes the class to CppVariantDouble.
cout << "Converted to Double: " << someNum->GetAsInt()
<< " (" << someNum->GetAsDouble() << ")" << endl;
return 0;
}

How to iterate through an unknown type?

I've got this function:
set<int> Search(const _Type & hayHeap) const {
set<int>myset;
for (vector<typename>::const_iterator i = needle.begin(); i != needle.end(); ++i) {
cout << (*i) << " ";
}
return myset;
};
and needle is defined like this:vector<string> needle;
Now I need to create another iterator, that will iterate through hayHeap. But the problem is, that I don't know, what type will it be. It could be a single string or vector of <int>/<string> as well. So when there is a string, it iterates only once, if there is some vector it iterates (myVector.count()-1)-times. How to make this type independent iterator?
In C++03:
template <typename C>
set<int> Search(const C& hayHeap) const {
set<int>myset;
for (typename C::const_iterator i = needle.begin(); i != needle.end(); ++i) {
cout << (*i) << " ";
}
return myset;
};
In C++11:
template <typename C>
set<int> Search(const C& hayHeap) const {
set<int>myset;
for (auto& i : needle) {
cout << i << " ";
}
return myset;
};
Depending on your actual needs, you'd replace int by typename C::value_type
OK I think I understood your question now. I think you are looking for some kind of type function. Something of this sort.
template<typename T>
struct my_selector {
static void search(T& t) {
cout << " Single string / int search" << endl;
bool b = t == "needle";
cout << b;
}
};
template<typename T>
struct my_selector<vector<T>> {
static void search(vector<T>& needle) {
cout << " Vector search" << endl;
for (typename vector<T>::const_iterator i = needle.begin();
i != needle.end(); ++i)
{
cout << (*i) << " ";
}
}
};
int main() {
typedef std::vector<std::string> _Type;
_Type needle(4,"s");
// Search function is selected based on type of _Type.
my_selector<_Type>::search(needle);
// typedef string _Type;
// _Type needle = "needle";
// // Search function is selected based on type of _Type.
// my_selector<_Type>::search(needle);
}

How to cheaply assign C-style array to std::vector?

Currently I do the following:
// float *c_array = new float[1024];
void Foo::foo(float *c_array, size_t c_array_size) {
//std::vector<float> cpp_array;
cpp_array.assign(c_array, c_array + c_array_size);
delete [] c_array;
}
How can I optimize this assigning? I would like not to perform elementwise copy but just swap pointers.
The current std::vector doesn't provide any capability or interface to take ownership of previously allocated storage. Presumably it would be too easy to pass a stack address in by accident, allowing more problems than it solved.
If you want to avoid copying into a vector, you'll either need to use vectors through your entire call chain, or do it the C way with float[] the entire time. You can't mix them. You can guaranteed that &vec[0] will be equivalent to the C-array though, fully contiguous, so using vector in the whole program may be feasible.
Currently, the std::vector interface does not possess the capacity to move from or swap with anything except another std::vector.
The only way to do it would be to create a custom allocator.
Write an allocator class that you can initialise with your array.
Instantiate the vector with the allocator as an argument.
Unlikely it's possible - it's quite dangerous, because std::vector doesn't know how the memory was allocated and how it should be freed.
If it's possible, you may replace original allocation with creation of std::vector of correct size. It uses contiguous memory area, so it can replace manually allocated buffer.
It is possible with the use of a custom allocator. I checked on godbolt.org with clang and gcc. To me, it looks a bit of an ugly hack – but it works at least as a proof of concept.
Of course, you have to take care of the lifetime of the array for yourself.
#include <vector>
#include <iostream>
// custom allocator
template <typename T>
class my_allocator {
private:
T* const addr;
public:
template <typename U>
struct rebind {
typedef my_allocator<U> other;
};
//provide the required no-throw constructors / destructors:
constexpr my_allocator(T* addr_) : addr(addr_) { };
constexpr my_allocator(const my_allocator<T>& rhs) : addr(rhs.addr) { };
template <typename U>
my_allocator(const my_allocator<U>& rhs, T* addr_) : addr(addr_) { };
~my_allocator() { };
//import the required typedefs:
using value_type=T;
using pointer=T*;
using reference=T&;
using const_pointer=const T*;
using const_reference=const T&;
using size_type=size_t;
using difference_type=ptrdiff_t;
constexpr pointer allocate(size_type n, const void * = 0) {
pointer t=addr;
std::cout
<< " used my_allocator to allocate at address "
<< t << " (+)" << std::endl;
return addr;
}
constexpr void deallocate(void* p, size_type) {
if (p) {
std::cout
<< " used my_allocator to deallocate at address "
<< p << " (-)" <<
std::endl;
}
}
template< class U, class... Args >
void construct( U* p, Args&&... args ) {
// avoids initialisation of the elements.
std::cout << "Contruct called" << std::endl;
}
};
// helper function for easy useage
template<typename T>
const std::vector<T, my_allocator<T> > CreateVectorFromArray(T* array, int size) {
const my_allocator<T> alloc=my_allocator<T>(array);
std::vector<int, my_allocator<int> > vecAll(size, my_allocator<int>(array));
return vecAll;
}
template<typename T>
using element_type_t = std::remove_reference_t<decltype(*std::begin(std::declval<T&>()))>;
template<typename AR>
auto CreateVectorFromArrayAr(AR& array) {
using T=element_type_t<AR>;
const my_allocator<T> alloc=my_allocator<T>(array);
std::vector<T, my_allocator<T> > vecAll(sizeof(array)/sizeof(array[0]), my_allocator<T>(array));
return vecAll;
}
int main() {
int array[]={0,1,2,3,4,5,6,7,8,9};
std::cout << "Array: " << &array[0] << " " << array[0] << " " << array[1]<< " " << array[2] << std::endl;
auto vecAll=CreateVectorFromArray(array, sizeof(array)/sizeof(array[0]));
auto vec3=CreateVectorFromArrayAr(array);
std::cout << "Vector: " << &vecAll[0] << " " << vecAll[0] << " " << vecAll[1]<< " " << vecAll[2] << std::endl;
std::cout << "Array: " << &array[0] << " " << array[0] << " " << array[1] << " " << array[2] << std::endl;
std::cout << "vec3: " << &vec3[0] << " " << vec3[0] << " " << vec3[1] << " " << vec3[2] << std::endl;
std::cout << "sizeof(vecAll)=" << sizeof(vecAll) << std::endl;
std::cout << "sizeof(void*)=" << sizeof(void*) << std::endl;
return 0;
}

container won't sort, test case included, (easy question?)

I can't see what I'm doing wrong. I think it might be one of the Rule of Three methods. Codepad link
#include <deque>
//#include <string>
//#include <utility>
//#include <cstdlib>
#include <cstring>
#include <iostream>
//#include <algorithm> // I use sort(), so why does this still compile when commented out?
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
using namespace std;
namespace fs = boost::filesystem;
class Page
{
public:
// constructor
Page(const char* path, const char* data, int size) :
path_(fs::path(path)),
size_(size),
data_(new char[size])
{
// cout << "Creating Page..." << endl;
strncpy(data_, data, size);
// cout << "done creating Page..." << endl;
}
// copy constructor
Page(const Page& other) :
path_(fs::path(other.path())),
size_(other.size()),
data_(new char[other.size()])
{
// cout << "Copying Page..." << endl;
strncpy(data_, other.data(), size_);
// cout << "done copying Page..." << endl;
}
// destructor
~Page() { delete[] data_; }
// accessors
const fs::path& path() const { return path_; }
const char* data() const { return data_; }
int size() const { return size_; }
// operators
Page& operator = (const Page& other) {
if (this == &other)
return *this;
char* newImage = new char[other.size()];
strncpy(newImage, other.data(), other.size());
delete[] data_;
data_ = newImage;
return *this;
}
bool operator < (const Page& other) const { return path_ < other.path(); }
private:
fs::path path_;
int size_;
char* data_;
};
class Book
{
public:
Book(const char* path) :
path_(fs::path(path))
{
cout << "Creating Book..." << endl;
cout << "pushing back #1" << endl;
pages_.push_back(Page("image1.jpg", "firstImage", 10));
cout << "pushing back #3" << endl;
pages_.push_back(Page("image3.jpg", "thirdImage", 10));
cout << "pushing back #2" << endl;
pages_.push_back(Page("image2.jpg", "secondImage", 11));
cout << "testing operator <" << endl;
cout << pages_[0].path().string() << (pages_[0] < pages_[1]? " < " : " > ") << pages_[1].path().string() << endl;
cout << pages_[1].path().string() << (pages_[1] < pages_[2]? " < " : " > ") << pages_[2].path().string() << endl;
cout << pages_[0].path().string() << (pages_[0] < pages_[2]? " < " : " > ") << pages_[2].path().string() << endl;
cout << "sorting" << endl;
BOOST_FOREACH (Page p, pages_)
cout << p.path().string() << endl;
sort(pages_.begin(), pages_.end());
cout << "done sorting\n";
BOOST_FOREACH (Page p, pages_)
cout << p.path().string() << endl;
cout << "done Creating Book" << endl;
}
private:
deque<Page> pages_;
fs::path path_;
};
int main() {
Book* book = new Book("/some/path/");
}
I just kept messing around, and realized that my assignment operator needs to copy all the other parameters over as well, not just the heap allocated ones.
Man do I feel dumb. >_<
Btw followup question: Is there a way to do the sorting without needing to strncpy() all the buffers and just swap the pointer addresses around instead?
edit:
tnx dirkgently. Yeah that's what it was, sry didn't see your comment before I posted this.