I have a class which stores a pointer to a data chunk and the size of the data. It implements operations like 'head', 'tail',... but it is irrelevant from the question's point of view. I use this instead of std::vector where I do not want deep copying.
However I need to copy the data sometimes, so I have a member function 'duplicate' to do it.
struct ByteArray {
char* data;
int size;
ByteArray(char* data, int size) : data(data), size(size) {}
ByteArray duplicate() const {
char *duplicatedData = new char[size];
memcpy(duplicatedData, data, size);
return ByteArray(duplicatedData, size);
}
};
Now I have a derived class - extending the previous class, where I need a duplicate method too, which calls the base class' duplicate. I have managed to solve it by casting both objects to the base class, but I suspect that this is not the best solution. Am I missing a more obvious solution? Also is this class the right solution to the original problem (not having deep copy) or there is standard solution what I'm not aware of?
struct Foo : ByteArray
{
int bar;
... // more members
Foo(ByteArray &bytes, int bar) : ByteArray(bytes), bar(bar) {}
Foo duplicate() const {
Foo dup = *this;
static_cast<ByteArray&>(dup) = ByteArray::duplicate();
return dup;
}
};
If you changed your Foo constructor to take a ByteArray by const reference instead, duplicate() would be pretty straightforward:
Foo duplicate() const {
return Foo(ByteArray::duplicate(), bar);
}
As-is, you can still do it the same way, just need an extra line:
Foo duplicate() const {
ByteArray ba = ByteArray::duplicate();
return Foo(ba, bar);
}
How about something like this then...
#include <cstring>
#include <iostream>
using namespace std;
struct ByteArray {
char* data;
int size;
ByteArray(char* data, int size) : data(data), size(size) {}
void duplicate(ByteArray &toCopy) const {
cout<<"Byte array duplicate."<<endl;
char *data = new char[toCopy.size];
memcpy(data, toCopy.data, size);
}
};
struct Foo : ByteArray
{
int bar;
Foo(ByteArray &bytes, int bar) : ByteArray(bytes), bar(bar) {}
void duplicate(Foo &toCopy) const {
cout<<"Foo duplicate."<<endl;
ByteArray::duplicate(toCopy);
}
};
int main(){
char* str = "some data";
char* null = "";
ByteArray barr (str, 9);
ByteArray barr2 = barr;
barr2.duplicate(barr);
Foo farr (barr, 2);
Foo farr2 = farr;
farr2.duplicate(farr);
}
Related
I hope to store the data in three different ways:
1.store to a std::string
2.write to file descriptor
3.both of the above
And I hope to use a uniform interface for these three different methods.
I wrote a simple code sippet to achieve the said goals. The first & second are easy indeed, but for the third I am stuck.
Please pay attention to the comment in the code snippet below, which is what the compiler complains if STORE_BY_FD_AND_STRING is defined.
Here is the code snippet:
#include <memory>
#include <iostream>
#include <unistd.h>
class DataStorage {
public:
DataStorage(int total_count):total_count_(total_count){};
virtual int Store(const char *buffer, int count) = 0;
virtual ~DataStorage(){};
protected:
int total_count_;
};
class DataStorageByStr : public DataStorage {
public:
DataStorageByStr(std::string &str) : str_(str), DataStorage(0){};
int Store(const char *buffer, int count)
{
str_ += std::string(buffer, count);
total_count_ += count;
return 0;
};
protected:
std::string &str_;
};
class DataStorageByFd : public DataStorage {
public:
DataStorageByFd(int &fd):fd_(fd), DataStorage(0){};
int Store(const char *buffer, int count)
{
int ret = write(fd_, buffer, count);
if(ret > 0)
{
total_count_ += ret;
}
return ret;
};
protected:
int &fd_;
};
class DataStorageByStrAndFd : public DataStorageByStr, public DataStorageByFd {
public:
DataStorageByStrAndFd(std::string &str, int &fd):DataStorageByStr(str), DataStorageByFd(fd) {}
int Store(const char *buffer, int count)
{
int ret1 = DataStorageByStr::Store(buffer, count);
int ret2 = DataStorageByFd::Store(buffer, count);
return ((0==ret1) && (0==ret2))?0:-1;
}
};
int StoreSomeData(DataStorage *pstorage, const std::string data_to_store)
{
return pstorage->Store(data_to_store.data(), data_to_store.length());
}
int main()
{
{
std::string str{"storing the string to std::string works !"};
std::string data;
DataStorage *pstorage = new DataStorageByStr(data);
StoreSomeData(pstorage, str);
std::cout << data << std::endl;
}
{
std::string str{"storing the string to fd works !"};
int fd = 1;
DataStorage *pstorage = new DataStorageByFd(fd);
StoreSomeData(pstorage, str);
}
#ifdef STORE_BY_FD_AND_STRING
{
std::string str{"thanks for your attention for this matter!"};
std::string data;
int fd = 1;
DataStorage *pstorage = new DataStorageByStrAndFd(str, fd); //The compiler complain that 'DataStorage' is an ambiguous base of 'DataStorageByStrAndFd'
StoreSomeData(pstorage, str);
}
#endif
}
Any sugestion to achieve all the aforementioned goals?
Thanks to #Sam Varshavchik #Adrian Mole. This code snippet works.
#include <memory>
#include <iostream>
#include <unistd.h>
class DataStorage {
public:
DataStorage(int total_count):total_count_(total_count){};
virtual int Store(const char *buffer, int count) = 0;
virtual ~DataStorage(){};
protected:
int total_count_;
};
class DataStorageByStr : virtual public DataStorage {
public:
DataStorageByStr(std::string &str) : str_(str), DataStorage(0){};
int Store(const char *buffer, int count)
{
str_ += std::string(buffer, count);
total_count_ += count;
return 0;
};
protected:
std::string &str_;
};
class DataStorageByFd :virtual public DataStorage {
public:
DataStorageByFd(int &fd):fd_(fd), DataStorage(0){};
int Store(const char *buffer, int count)
{
int ret = write(fd_, buffer, count);
if(ret > 0)
{
total_count_ += ret;
}
return ret;
};
protected:
int &fd_;
};
class DataStorageByStrAndFd : public DataStorageByStr, public DataStorageByFd {
public:
DataStorageByStrAndFd(std::string &str, int &fd):DataStorageByStr(str), DataStorageByFd(fd), DataStorage(0)
{
}
int Store(const char *buffer, int count)
{
int ret1 = DataStorageByStr::Store(buffer, count);
int ret2 = DataStorageByFd::Store(buffer, count);
return ((0==ret1) && (0==ret2))?0:-1;
}
};
int StoreSomeData(DataStorage *pstorage, const std::string data_to_store)
{
return pstorage->Store(data_to_store.data(), data_to_store.length());
}
int main()
{
{
std::string str{"storing the string to std::string works !"};
std::string data;
DataStorage *pstorage = new DataStorageByStr(data);
StoreSomeData(pstorage, str);
std::cout << data << std::endl;
}
{
std::string str{"storing the string to fd works !"};
int fd = 1;
DataStorage *pstorage = new DataStorageByFd(fd);
StoreSomeData(pstorage, str);
}
#ifndef STORE_BY_FD_AND_STRING
{
std::string str{"thanks for your attention for this matter!"};
std::string data;
int fd = 1;
DataStorage *pstorage = new DataStorageByStrAndFd(str, fd); //The compiler complain that 'DataStorage' is an ambiguous base of 'DataStorageByStrAndFd'
StoreSomeData(pstorage, str);
}
#endif
}
Explanation:
Virtual inheritance is a C++ technique that ensures only one copy of a base class's member variables are inherited by grandchild derived classes. Without virtual inheritance, if two classes B and C inherit from a class A, and a class D inherits from both B and C, then D will contain two copies of A's member variables: one via B, and one via C. These will be accessible independently, using scope resolution.
Instead, if classes B and C inherit virtually from class A, then objects of class D will contain only one set of the member variables from class A.
This feature is most useful for multiple inheritance, as it makes the virtual base a common subobject for the deriving class and all classes that are derived from it. This can be used to avoid the diamond problem by clarifying ambiguity over which ancestor class to use, as from the perspective of the deriving class (D in the example above) the virtual base (A) acts as though it were the direct base class of D, not a class derived indirectly through a base (B or C).
In the following code I made a template class, Its initialized in main function and I'm trying to assign char* as you can see below but It isn't working. I think the issue is in assign operator function I defined in Proxy class but I can't figure it out
#include <iostream>
using namespace std;
template <class T>
class Vector {
public:
T *p;
Vector(int size) {
p = new T[size];
}
class Proxy {
Vector &a;
int i;
public:
Proxy(Vector &a, int i) : a(a), i(i) {
}
void operator=(const T x) {
a.p[i] = x;
}
};
Proxy operator[](int i) {
return Proxy(*this, i);
}
};
int main() {
Vector<char *> sv1(2);
sv1[0] = "John";
sv1[1] = "Doe";
}
I'm getting following error;
I already tried setting parameter in assignment operator function to const, I also tried implicitly typecasting to T nothing has worked
Try this:
using namespace std;
template <class T>
class Vector {
public:
T* p;
int sz;
Vector(int size) {
p = new T[size];
sz = size;
}
template<class T>
class Proxy {
Vector<T>& v;
int i;
public:
Proxy(Vector<T>& vec, int index) :v(vec),i(index) { }
void operator= (const T val) { v.p[i] = val; }
};
Proxy<T> operator[](int index) { return Proxy<T>(*this, index); }
};
Your code will work with any basic type, (int, char, double) and pointers, but not, for example, with this:
int main() {
Vector<char*> sv1(2);
sv1[0] = "John";
sv1[1] = "Doe";
}
Firstly, the Vector points to a char*, not a string literal (const char*). You'd have to cast it using a C-style cast or a const_cast. Example:
int main() {
Vector<char*> sv1(2);
sv1[0] = const_cast<char*>("John"); //succeeds
sv1[1] = (char*)"Doe"; //succeeds
sv1[0] = "John"; //fails
sv1[1] = "Doe"; //fails
}
A string literal is always a const char* in C++.
You'll have same error writing code:
char * whatever = "something";
This code is absolutely wrong at least for string:
void operator=(const T x)
{
a.p[i] = x;
}
Step 1: allocate buffer;
Step 2: copy string to allocated buffer.
Your code is OK for primitives like char, int, etc. The following code should work:
int main() {
Vector<char> sv1(2);
sv1[0] = 'J';
sv1[1] = 'D';
}
I have a simple class which consists of a void pointer and an int (this is some sort of a boost::Variant educational project).
I also have a working copy constructor and a destructor.
But what grinds my gears is, how I would accomplish something like this:
Container cont1("some value"); //simple construction
Container cont2;
cont2.createLink(cont1); //this should initialize members with a reference (or something alike)
std::cout<<cont1; //yields "some value"
cont2.set(20); //setting this container should update the original container too, since I initialized with a reference (or smth alike)
std::cout<<cont1; //yields 20
This is the simplified version of the class:
class Container {
public:
Container(){}
Container(const std::string &val){var.type = STRING; var.data = new std::string(val);}
Container(int val){ /* same for int */}
Container(const Container &val){ /* do a memory copy */}
void set(int val){ /* set the value if type matches, otherwise allocate a new pointer */}
void set(const std::string &val){ /* the same as for int */}
void createLink(const Container &val){ /* somehow assign a reference or whatsoever */}
private:
typedef struct VAR {
int type = 0;
void *data = NULL; }
VAR var;
}
If I set the value of cont2 to a string (i.e. the same data type it holds at the moment), everything is fine, because the set would not allocate a new pointer and rather assign a new value.
But how do I make sure the pointer of cont1 updates if I assign a different value to cont2 and therefore have to allocate a new pointer?
Would I need something like shared_pointer?
Thanks for any insight!
EDIT:
I changed to function name to make it more clear what should happen.
There is a solution that only involves straight OO. You could create an interface for your variant type, and use double indirection to the variant instance to allow linked containers to share the same variant instance.
The reason double indirection is required is because of the way you want the set() method to automatically allocate a new variant instance if the new type doesn't match the original type. If we simply shared a pointer to the variant from both containers, then after set() creates a new variant instance, each container would be referring to different instances again.
To get around that, we can use a pointer to a pointer to a variant in the container instead.
Here is a possible way to define your variant interface, and how it could be subclassed:
typedef std::ostream Out;
struct BadType {};
struct Var {
virtual ~Var () = default;
virtual Out & print (Out &os) { return os << "(BadType)"; }
virtual void set (int) { throw BadType(); }
virtual void set (const std::string &) { throw BadType(); }
};
struct VarInteger : Var {
int data;
VarInteger (int v) : data(v) {}
Out & print (Out &os) { return os << data; }
void set (int v) throw() { data = v; }
};
struct VarString : Var {
std::string data;
VarString (const std::string &v) : data(v) {}
Out & print (Out &os) { return os << data; }
void set (const std::string &v) throw() { data = v; }
};
Here is how you could define your pointer to pointer, and how they could be initialized:
typedef std::shared_ptr<Var> VarPtr;
std::shared_ptr<VarPtr> varptr_;
static VarPtr make_var () { return std::make_shared<Var>(); }
static VarPtr make_var (int v) { return std::make_shared<VarInteger>(v); }
static VarPtr make_var (const std::string &v) {
return std::make_shared<VarString>(v);
}
VarPtr & var () { return *varptr_; }
const VarPtr & var () const { return *varptr_; }
Container () : varptr_(std::make_shared<VarPtr>(make_var())) {}
Container (int v) : varptr_(std::make_shared<VarPtr>(make_var(v))) {}
Container (const std::string &v)
: varptr_(std::make_shared<VarPtr>(make_var(v))) {}
And here is how your set() methods and createLink() method could be implemented.
void set (int v) {
try { var()->set(v); }
catch (BadType) { var() = make_var(v); }
}
void set (const std::string &v) {
try { var()->set(v); }
catch (BadType) { var() = make_var(v); }
}
void createLink (const Container &val) { varptr_ = val.varptr_; }
Demo
How about the following. Of course createLink cannot not take a const reference so I made it to take a non-const pointer.
class Container {
const int STRING = 0x0000001;
const int INT = 0x0000002;
const int LINK = 0x8000000;
public:
...
void set(int val){...}
void set(const std::string &val)
{
if (var.type == LINK)
{
reinterpret_cast<Container*>(var.data)->set(val);
}
else
...
}
void createLink(Container* val)
{
var.data = val;
var.type = LINK;
}
private:
typedef struct VAR {
int type = 0;
void *data = NULL;
};
VAR var;
};
There are a some important points to think about - relative lifetimes of the link and the linked is the most obvious one.
Is there a way in C++03 (or earlier) to write a class that can either store a const or non-const pointer, and handles access appropriately? Take the usage of the non-functional "SometimesConst" class as an example:
class SometimesConst
{
public:
SometimesConst(int * buffer) : buffer(buffer) {} // Needs const qualifier?
int* get() { return buffer; } // Needs const qualifier?
void increment() { counter++; }
private:
int * buffer; // Needs const qualifier?
int counter;
};
void function(int * n, const int * c)
{
// These are both okay
SometimesConst wn(n);
SometimesConst wc(c);
// Reading the value is always allowed
printf("%d %d", wn.get()[0], wc.get()[0]);
// Can increment either object's counter
wn.increment();
wc.increment();
// Can set non-const pointer
wn.get()[0] = 5;
// Should generate a compiler error
wc.get()[0] = 5;
}
Creating a const SometimesConst would not allow modification of the counter property of the object. Can a class be designed that has compile-time const safety for input objects, only if they are passed in as const?
No, not the way you are wanting to use it. The only way to have different behavior at compile time is to have different types. However, you can make that fairly easy to use:
#include <stdio.h>
template <typename T>
class SometimesConst
{
public:
SometimesConst(T* buffer) : buffer(buffer) { }
T* get() { return buffer; }
void increment() { ++counter; }
private:
T *buffer;
int counter;
};
typedef SometimesConst<const int> IsConst;
typedef SometimesConst<int> IsNotConst;
void function(int * n, const int * c)
{
IsNotConst wn(n);
IsConst wc(c);
// Reading the value is always allowed
printf("%d %d", wn.get()[0], wc.get()[0]);
// Can increment either object's counter
wn.increment();
wc.increment();
// Can set non-const pointer
wn.get()[0] = 5;
// Should generate a compiler error
wc.get()[0] = 5;
}
The language already mostly lets you do this with a simple class; with the way const cascades to access to members (combined with mutable for the counter member, which you've indicated should always be mutable), you can provide both read-only and read-write access to a buffer quite easily:
class C
{
public:
C(int* buffer) : buffer(buffer) {}
const int* get() const { return buffer; }
int* get() { return buffer; }
void increment() const { counter++; }
private:
int* buffer;
mutable int counter;
};
void function(int* n)
{
// These are both okay
C wn(n);
const C wc(n);
// Reading the value is always allowed
printf("%d %d", wn.get()[0], wc.get()[0]);
// Can increment either object's counter
wn.increment();
wc.increment();
// Can set non-const pointer
wn.get()[0] = 5;
// Generates a compiler error
wc.get()[0] = 5;
}
What you can't do with this is neatly arrange for the class to be instantiated with either a int* or a const int*; the two lead to totally different semantics for your class, so you should split it into two if you really need that.
Fortunately, templates make this easy:
template <typename T>
class C
{
public:
C(T* buffer) : buffer(buffer) {}
const T* get() const { return buffer; }
T* get() { return buffer; }
void increment() const { counter++; }
private:
T* buffer;
mutable int counter;
};
Now a C<int> is as above, but a C<const int> only provides read-only access to the buffer, even when the C<const int> object itself is not marked as const:
void function(int* n1, const int* n2)
{
C<int> a(n1);
C<const int> b(n2);
const C<int> c(n1);
const C<const int> d(n2);
// Reading the value is always allowed
printf("%d %d %d %d",
a.get()[0], b.get()[0],
c.get()[0], d.get()[0]
);
// Incrementing the counter is always allowed
a.increment();
b.increment();
c.increment();
d.increment();
// Can set non-const pointer
a.get()[0] = 5;
// Cannot set const pointer, or const/non-const pointer behind const object
//b.get()[0] = 5;
//c.get()[0] = 5;
//d.get()[0] = 5;
}
Live demo
I think that there is a design problem if you want to store two different things which must be handled in different ways in one class. But yes, you can do it:
struct X{};
class A
{
public:
A(const X*) { cout << "const" << endl; }
A(X*) { cout << "non const" << endl; }
};
int main()
{
const X x1;
X x2;
A a1(&x1);
A a2(&x2);
}
the output is expected:
const
non const
I'm working with some legacy code and I need a StoreStrings class storing
some strings and able to return a MyString*.
I've tried with this:
typedef char MyString[64];
class StoreStrings{
public:
void store(MyString *aStr)
{
theVec.push_back(aStr);
}
const MyString* get(){return theVec.begin();}
private:
std::vector<MyString> theVec;
};
But I'm disappointed since it doesn't compile with this syntax.
StoreStrings myStore;
myStore.store("Hello");//cannot convert parameter 1 from 'char [6]' to 'char (*)[64]'
I've to instantiate one MyString before.
MyString myStr = "Hello";
myStore.store(&myStr);
How can I rewrite the StoreStrings class so to have myStore.store("Hello"); compiling?
Arrays cannot be used in STL containers as it requires the type to be copy constructible and assignable
You may try following, however std::string approach is best.
typedef char MyString[64];
struct X{
MyString s;
};
class StoreStrings{
public:
void store(MyString aStr)
{
X temp ;
for(int i=0;aStr[i];++i)
temp.s[i] =*(aStr+i);
theVec.push_back(temp);
}
// Here iterator is returned.
const std::vector<X>::iterator get(){return theVec.begin();}
private:
std::vector<X> theVec;
};
int main(){
StoreStrings myStore;
MyString m ="Hello";
myStore.store(m);
}
I would suggest something like that
Storestring.h
#pragma once
#include <vector>
class StoreStrings
{
public:
void store(const char* aStr)
{
pszStr = new char[64];
strcpy_s(pszStr,64,aStr);
theVec.push_back(pszStr);
};
~StoreStrings(void){
for(std::vector<char*>::iterator it = theVec.begin();it!=theVec.end();++it){
delete *it;
}
};
std::vector<char*>::iterator getBegin(){return theVec.begin();};
std::vector<char*>::iterator getEnd(){return theVec.end();};
private:
char* pszStr;
std::vector<char*> theVec;
};
main.cpp
#include "StoreStrings.h"
#include <iostream>
int main(void){
StoreStrings s;
s.store("a");
s.store("b");
s.store("c");
for(std::vector<char*>::iterator it = s.getBegin();it!=s.getEnd();++it){
std::cout << *it<<std::endl;
}
return 0;
};