I want to implement a minHeap in c++ for char[] buffers and am facing some problems with the implementation. My declaration of the priority queue is as follows (I am not sure if this will give me a maxHeap or a minHeap):
priority_queue<char[], vector<char[]>, comparePacketContents> receiveBuffer;
where comparePacketContents is:
struct comparePacketContents {
bool operator()(char lhs[], char rhs[]) const {
return atoi(TcpPacket::getBytes(lhs, 0, SEQUENCE_SIZE)) < atoi(TcpPacket::getBytes(rhs, 0, SEQUENCE_SIZE));
}
};
and TcpPacket::getBytes is:
char* TcpPacket::getBytes(char* buf, int start, int size) {
char* ans = (char *) malloc(sizeof(char)*size);
for (int i = 0; i < size; i++) {
*(ans + i) = *(buf + start + i);
}
return ans;
}
Basically I intend to get the first SEQUENCE_SIZE characters of the received packet and then create a heap ordered upon the value of the sequence number.
However, when I try to push a packet into this heap using:
receiveBuffer.push(buf);
It gives me the following error:
no instance of overloaded function "std::priority_queue<_Ty, _Container, _Pr>::push [with _Ty=char [], _Container=std::vector<char [], std::allocator<char []>>, _Pr=comparePacketContents]" matches the argument list
argument types are: (char [2048])
object type is: std::priority_queue<char [], std::vector<char [], std::allocator<char []>>, comparePacketContents>
What should I do to resolve this error?
You can probably "fix" the compilation error by doing push(&buf) to push a pointer to the beginning of the array explicitly. Otherwise the compiler thinks you want to push the entire array, while the container holds pointers (char[] is like char*).
But probably that's not sufficient to fix all the problems you have, because it seems you are storing raw pointers to C-style strings without managing those allocations correctly. Instead, consider writing a class to hold your packets:
class Packet {
public:
Packet(const char* data); // takes ownership of data
uint32_t seqnum() const; // similar to existing implementation
// ...
private:
std::shared_ptr<char> m_data;
};
Packet::Packet(const char* data) : m_data(data, free) {
}
bool operator<(const Packet& lhs, const Packet& rhs) {
return lhs.seqnum() < rhs.seqnum();
}
priority_queue<Packet> receiveBuffer;
In my example I assume you release packet buffers using the C free() function, but you can use any "deleter" in the C++ shared_ptr constructor, including one you write yourself.
Related
It might not be advisable according to what I have read at a couple of places (and that's probably the reason std::string doesn't do it already), but in a controlled environment and with careful usage, I think it might be ok to write a string class which can be implicitly converted to a proper writable char buffer when needed by third party library methods (which take only char* as an argument), and still behave like a modern string having methods like Find(), Split(), SubString() etc. While I can try to implement the usual other string manipulation methods later, I first wanted to ask about the efficient and safe way to do this main task. Currently, we have to allocate a char array of roughly the maximum size of the char* output that is expected from the third party method, pass it there, then convert the return char* to a std::string to be able to use the convenient methods it allows, then again pass its (const char*) result to another method using string.c_str(). This is both lengthy and makes the code look a little messy.
Here is my very initial implementation so far:
MyString.h
#pragma once
#include<string>
using namespace std;
class MyString
{
private:
bool mBufferInitialized;
size_t mAllocSize;
string mString;
char *mBuffer;
public:
MyString(size_t size);
MyString(const char* cstr);
MyString();
~MyString();
operator char*() { return GetBuffer(); }
operator const char*() { return GetAsConstChar(); }
const char* GetAsConstChar() { InvalidateBuffer(); return mString.c_str(); }
private:
char* GetBuffer();
void InvalidateBuffer();
};
MyString.cpp
#include "MyString.h"
MyString::MyString(size_t size)
:mAllocSize(size)
,mBufferInitialized(false)
,mBuffer(nullptr)
{
mString.reserve(size);
}
MyString::MyString(const char * cstr)
:MyString()
{
mString.assign(cstr);
}
MyString::MyString()
:MyString((size_t)1024)
{
}
MyString::~MyString()
{
if (mBufferInitialized)
delete[] mBuffer;
}
char * MyString::GetBuffer()
{
if (!mBufferInitialized)
{
mBuffer = new char[mAllocSize]{ '\0' };
mBufferInitialized = true;
}
if (mString.length() > 0)
memcpy(mBuffer, mString.c_str(), mString.length());
return mBuffer;
}
void MyString::InvalidateBuffer()
{
if (mBufferInitialized && mBuffer && strlen(mBuffer) > 0)
{
mString.assign(mBuffer);
mBuffer[0] = '\0';
}
}
Sample usage (main.cpp)
#include "MyString.h"
#include <iostream>
void testSetChars(char * name)
{
if (!name)
return;
//This length is not known to us, but the maximum
//return length is known for each function.
char str[] = "random random name";
strcpy_s(name, strlen(str) + 1, str);
}
int main(int, char*)
{
MyString cs("test initializer");
cout << cs.GetAsConstChar() << '\n';
testSetChars(cs);
cout << cs.GetAsConstChar() << '\n';
getchar();
return 0;
}
Now, I plan to call the InvalidateBuffer() in almost all the methods before doing anything else. Now some of my questions are :
Is there a better way to do it in terms of memory/performance and/or safety, especially in C++ 11 (apart from the usual move constructor/assignment operators which I plan to add to it soon)?
I had initially implemented the 'buffer' using a std::vector of chars, which was easier to implement and more C++ like, but was concerned about performance. So the GetBuffer() method would just return the beginning pointer of the resized vector of . Do you think there are any major pros/cons of using a vector instead of char* here?
I plan to add wide char support to it later. Do you think a union of two structs : {char,string} and {wchar_t, wstring} would be the way to go for that purpose (it will be only one of these two at a time)?
Is it too much overkill rather than just doing the usual way of passing char array pointer, converting to a std::string and doing our work with it. The third party function calls expecting char* arguments are used heavily in the code and I plan to completely replace both char* and std::string with this new string if it works.
Thank you for your patience and help!
If I understood you correctly, you want this to work:
mystring foo;
c_function(foo);
// use the filled foo
with a c_function like ...
void c_function(char * dest) {
strcpy(dest, "FOOOOO");
}
Instead, I propose this (ideone example):
template<std::size_t max>
struct string_filler {
char data[max+1];
std::string & destination;
string_filler(std::string & d) : destination(d) {
data[0] = '\0'; // paranoia
}
~string_filler() {
destination = data;
}
operator char *() {
return data;
}
};
and using it like:
std::string foo;
c_function(string_filler<80>{foo});
This way you provide a "normal" buffer to the C function with a maximum that you specify (which you should know either way ... otherwise calling the function would be unsafe). On destruction of the temporary (which, according to the standard, must happen after that expression with the function call) the string is copied (using std::string assignment operator) into a buffer managed by the std::string.
Addressing your questions:
Do you think there are any major pros/cons of using a vector instead of char* here?
Yes: Using a vector frees your from manual memory management. This is a huge pro.
I plan to add wide char support to it later. Do you think a union of two structs : {char,string} and {wchar_t, wstring} would be the way to go for that purpose (it will be only one of these two at a time)?
A union is a bad idea. How do you know which member is currently active? You need a flag outside of the union. Do you really want every string to carry that around? Instead look what the standard library is doing: It's using templates to provide this abstraction.
Is it too much overkill [..]
Writing a string class? Yes, way too much.
What you want to do already exists. For example with this plain old C function:
/**
* Write n characters into buffer.
* n cann't be more than size
* Return number of written characters
*/
ssize_t fillString(char * buffer, ssize_t size);
Since C++11:
std::string str;
// Resize string to be sure to have memory
str.resize(80);
auto newSize = fillSrting(&str[0], str.size());
str.resize(newSize);
or without first resizing:
std::string str;
if (!str.empty()) // To avoid UB
{
auto newSize = fillSrting(&str[0], str.size());
str.resize(newSize);
}
But before C++11, std::string isn't guaranteed to be stored in a single chunk of contiguous memory. So you have to pass through a std::vector<char> before;
std::vector<char> v;
// Resize string to be sure to have memor
v.resize(80);
ssize_t newSize = fillSrting(&v[0], v.size());
std::string str(v.begin(), v.begin() + newSize);
You can use it easily with something like Daniel's proposition
Currently I working on a existing project (DLL ) which I have to extend.
For the transport through the DLL I have a struct for example 'ExternEntry'
and a struct which passes a array of it.
struct ExternEntry
{
unsigned int MyInt;
const wchar_t* Text;
}
struct ExternEntries
{
const ExternEntry* Data;
const unsigned int Length;
ExternEntries(const ExternEntry* ptr, const unsigned int size)
: Data(ptr)
, Length(size);
{
}
}
In the existing project architecture, it will be the first time that a array is passed to the DLL callers. So the existing architecture doesn't allow arrays and if a struct is passed to a caller, normally there is a wrapper-struct for it (because of their str pointers).
Inside the DLL I need to wrap the ExternEntry so have a valid Text pointer.
struct InternEntry
{
ExternEntry Data;
std::wstring Text;
inline const ExternEntry* operator&() const { return& Data }
UpdateText() { Data.Text = Text.c_str(); }
}
struct InternEntries
{
std::vector<InternEntry> Data;
operator ExternEntries() const
{
return ExternEntries(Data.data()->operator&(), Data.size());
}
}
So the problem is, when the Caller received the ExternEntries and created a vector again:
auto container = DllFuncReturnInternEntries(); // returns ExternEntries
std::vector<ExternEntry> v(container.Data, container.Data + container.Length);
The first element is valid. All other elements are pointing to the wrong memory because in memory the InternEntry (with the wstring Text) is stored between the next InternEntry.
Maybe I'm wrong with the reason why this can't work.
[Data][std::wstring][Data][std::wstring][Data][std::wstring]
Caller knows just about the size of the [Data]
So the vector is doing the following:
[Data][std::wstring][Data][std::wstring][Data][std::wstring]
| | |
Get Get Get
instead of
[Data][std::wstring][Data][std::wstring][Data][std::wstring]
| | |
Get Get Get
Do I have any possibilities to customize how the vector stores InternEntry objects in memory?
like Data,Data,Data ..anywhere else wstring,wstring,wstring
I hope I have explained my problem well
I have a class that wraps a big array of bytes that are network packets. The class implements a queue and provides (among others) front() function that returns a const vector of bytes that constitute the oldest packet in the queue.
class Buffer{
unsigned char data[65536];
unsigned int offset;
unsigned int length;
[...]//other fields for maintaining write ptr etc.
public:
const std::vector<unsigned char> front(){
return std::vector<unsigned char>(data + offset, data + offset + length);
}
//other methods for accessing the queue like
//pop(), push(), clean() and so forth...
[...]
}
The performance of above implementation of front() function suffers from unnecessary copying bytes from the range occupied by the current packet. Since the vector is const, there is no need of making a copy of the data. What I want is to create a vector on the data that are already stored in the buffer. Of course destructor of the vector should not deallocate the memory.
You have some options available to you:
Rather than returning a vector, just return a const char*:
const char* front() {
return data;
}
Consider using a standard container, such as a string data as your Buffer member. This will allow you to do:
const string& front() {
return data;
}
The best option though is if you have C++17 or access to experimental::string_view you could just do:
const string_view front() {
return string_view(data);
}
Just a convention comment, there is going to be an expectation of front that it will behave like other standard containers, which:
Returns a reference to the first element in the container.
Calling front on an empty container is undefined.
[source]
Bringing front to apply to bare on fixed size arrays was also discussed by the C++ standards committee: front and back Proposal for iterators Library
As it is this method more closely resembles data, which:
Returns a pointer to the block of memory containing the elements of the container.
[source]
If you're looking to avoid unnecessary copying then you'll need to return a view into the data. You can either provide a front_begin() and front_end() set of functions:
const char *front_begin() const
{
return data + offset;
}
const char *front_end() const
{
return data + offset + length;
}
Or write a wrapper class:
class Data
{
private:
const char *m_Begin;
const char *m_End;
public:
Data(const char *begin, const char *end) : m_Begin(begin), m_End(end)
{
}
const char *begin() const
{
return m_Begin;
}
const char *end() const
{
return m_End;
}
}
And have your front() method return one of these:
Data front()
{
return Data(data + offset, data + offset + length)
}
If you're using C++11 then you can use a Data instance in a ranged based for loop:
Data data = buffer.front();
for(char c : data)
{
// Do something with the data
}
Problem: I need to write/read objects from a file.This because I need to write/read a std::list to file, but in whatever case.Not only with T=int (this would be simple), but with whatever parameter.
In Java with OutputFileStream and InputFileStream this was possibile, but I suppose it's just a JVM feature.
However I am trying to read/write objects to a file:
template <class T>
bool write_object(std::fstream& out, const T& object)
{
bool result=false;
char* ptr;
const unsigned long size=sizeof(object);
if(out.good())
{
result=true;
ptr=(char*)&object;
for(unsigned int i=0;i<size;i++)
{
out << *ptr;
ptr++;
}
}
return result;
}
template <class T>
bool read_object(std::fstream& in, T& object)
{
bool result=false;
T* ptr;
T swap_temp;
const unsigned long size=sizeof(object);
char temp[size];
std::streampos pos;
if(in.good())
{
pos=in.tellg();
if(pos!=-1)
{
result=true;
for(unsigned long i=0; i<size; i++)
{
if(!in.good())
{
result=false;
break;
}
else
{
in >> temp[i];
}
}
}
}
if(result==false)
{
in.seekg(pos);
}
else
{
ptr=(T*)temp;
swap_temp=*ptr;
object=swap_temp;
}
return result;
}
But I have encountered the following problems:
-sizeof operator just returns the size of all fields, it does not consider also the data pointed by internal fields;
-If in the class there is a pointer, then this pointer could point to a "wrong" memory address, (e.g.) if I use a pointer which points to a C-style string in memory, once the program ends the string is deallocated.When the instance of the program runs again,this area of memory could be anywhere.
This method is wrong because for example sizeof(string) with my compiler returns 4.
So I suppose it uses a char pointer (I am on a 32-bit machine) to point to the C-style string allocated.Probably it does not even keep trace of the length.
So if the string has 32 characters I don't notice it, it just copies the value of the pointer.
Your approach can't work since C++ doesn't know java-like techniques like reflection so you can't distinguish between pointers and other members.
What you want is called serialisazion and you can use it with libraries like Boost.Serialization (Demo).
But even then, you can't write a general function, you have to define it specifically for each object.
This is a homework assignment. The Field container was the assignment from a week ago, and now I'm supposed to use the Field container to act as a dynamic array for a struct NumPair which holds two char * like so:
struct NumPair
{
char *pFirst, *pSecond;
int count;
NumPair( char *pfirst = "", char *psecond = "", int count = 0)
: pFirst(strdup(pfirst)), pSecond(strdup(psecond)), count(count)
{ }
NumPair( const NumPair& np )
: count(np.count), pFirst(strdup(np.pFirst)), pSecond(strdup(np.pSecond))
{ }
NumPair& operator=( const NumPair& np )
{
if(this != &np)
{
pFirst = strdup(np.pFirst);
pSecond = strdup(np.pSecond);
count = np.count;
}
return *this;
}
and the Field container
Field<NumPair> dict_;
The homework requires the use of char *, and not string, so that we can get better with all this low-level stuff. I've already had some question about char to wchar_t conversions, etc.
Now I have a question as to whether or not I'm destructing the NumPair properly. The scenario is as follows:
1) Field destructor gets called
template <class T>
Field<T>::~Field()
{
delete[] v_;
}
2) Delete calls the destructor of every element NumPair in v_;
~NumPair()
{
free(pFirst);
free(pSecond);
}
Is this okay? I haven't really read too many articles about mixing and matching elements created on the heap and free-store as we wish. I figure as long as I don't use delete on an improper malloc'ed element, I should be fine.
However, I don't know the entire intricacies of the delete command, so I'm wondering whether or not this is valid design, and what I could do to make it better.
Also, of course this isn't. I'm getting an error of the type:
This may be due to a corruption of the heap and points to dbgheap
extern "C" _CRTIMP int __cdecl _CrtIsValidHeapPointer(
const void * pUserData
)
{
if (!pUserData)
return FALSE;
if (!_CrtIsValidPointer(pHdr(pUserData), sizeof(_CrtMemBlockHeader), FALSE))
return FALSE;
return HeapValidate( _crtheap, 0, pHdr(pUserData) ); // Here
}
Again, how could I improve this without the use of string?
FIELD CTOR/Copy Ctor/Assignment
template <class T>
Field<T>::Field()
: v_(0), vused_(0), vsize_(0)
{ }
template <class T>
Field<T>::Field(size_t n, const T &val)
: v_(0), vused_(n), vsize_(0)
{
if(n > 0)
{
vsize_ = 1;
while(vsize_ < n)
vsize_ <<= 1;
v_ = new T[vsize_];
std::fill(v_, (v_ + vused_), val);
}
}
template <class T>
Field<T>::Field(const Field<T> &other)
: v_(new T[other.vsize_]), vsize_(other.vsize_), vused_(other.vused_)
{
std::copy(other.v_, (other.v_ + other.vused_), v_);
}
template <class T>
Field<T>& Field<T>::operator =(const Field<T> &other)
{
this->v_ = other.v_;
this->vused_ = other.vused_;
this->vsize_ = other.vsize_;
return *this;
}
FIELD MEMBERS
T *v_;
size_t vsize_;
size_t vused_;
Your copy constructor (of Field<>) seems OK, but the operator= is problematic.
Not only does it leak memory (what happens to the original v_?), but after that, two instances of Field<> hold a pointer to the same block of memory, and the one that is destructed first will invalidate the others v_ - and you can't even tell whether that has happened.
It's not always easy to decide how to deal with operator= - some think that implicit move semantics are okay, but the rest of us see how that played out with the majority of people, with std::auto_ptr. Probably the easiest solution is to disable copying altogether, and use explicit functions for moving ownership.
Your string handling in NumPair looks ok (strdup + free) and your Field container delete[] looks okay but it's hard to say because you don't show what v_ is.
eq mentions in a comment that you should also beware of how you are copying NumPairs. By default, C++ will give you an implicit member-wise copy constructor. This is where a RAII type like std::string makes your life easier: Your std::string containing struct can be copied without any special handling on your part and memory referenced in the string will be taken care of by the string's copy. If you duplicate your NumPair (by assigning it or returning it from a function for example) then the destruction of the temporary will free your strings out from under you.
Your copy constructor for Field just copies the pointers in v_. If you have two copies of a Field, all of the NumPairs in v_ will be deleted when the first Field goes out of scope, and then deleted again when the second one does.