Im new to shared_ptr and can't wrap my around this,
I want to store shared_ptr in custom struct.
struct ThreadSafeQMsg
{
ThreadSafeQMsg(shared_ptr<ptree> control, shared_ptr<uint32_t> data=0, int data_size=0) {
data_size = data_size;
data = data;
control = control;
}
size_t data_size;
shared_ptr<ptree> control;
shared_ptr<uint32_t> data;
};
ThreadSafeQMsg* tmsg = new ThreadSafeQMsg(control,spData, data_size);
PRINT_LOG("message with data[" << std::dec << tmsg->data_size << ",ref: " << std::dec << tmsg->data.use_count()
<< "] & control[ref: " << std::dec << tmsg->control.use_count() << "]");
before assignment I see the that the ref count is 1
when checking after assignment I get:
message with data[140737018606128,ref: 0] & control[ref: 0]
what am I missing here?
Inside the constructor, the names of parameters hide the names of data members; so these assignments like data_size = data_size; are just assigning the parameters to themselves and have nothing to do with the data members, which are kept default-initialized.
You can initialize them in member initializer list as
ThreadSafeQMsg(shared_ptr<ptree> control, shared_ptr<uint32_t> data=0, int data_size=0)
: data_size(data_size),
control(control),
data(data) {
}
Or if you want to stick to assignment (but applying member initializer list is better in general).
ThreadSafeQMsg(shared_ptr<ptree> control, shared_ptr<uint32_t> data=0, int data_size=0) {
this->data_size = data_size;
this->data = data;
this->control = control;
}
I'm trying to figure out the best approach for transferring some data over the network. Here is what i'm hoping to achieve:
The application runs and computes some data:
int w = 5;
float x = 4.736;
std::string y = "Some String.";
std::vector<int> z;
z.push_back(1);
z.push_back(2);
z.push_back(3);
Then we put it in a binary container:
BinaryContainer Data;
Data.Write(w);
Data.Write(x);
Data.Write(y);
Data.Write(z);
We then transfer it over the network:
SendData(Data.c_str());
And read it out on the other side:
BinaryContainer ReceivedData(IncomingData);
int w = ReceivedData.Read();
float x = ReceivedData.Read();
std::string y = ReceivedData.Read();
std::vector<int> z = ReceivedData.Read();
The example above outlines how the basic functionality from a high level perspective should work. I've looked at many different serialization libraries and none seem to fit quite right. I'm leaning towards learning how to write the functionality myself.
Endianness doesn't matter. The architecture that reads and writes data will never differ.
We only need to store binary data inside the container. The reading application and writing application is exclusively responsible for reading data in the same order it was written. Only basic types need to be written, no entire arbitrary classes or pointers to things. Most importantly overall the speed in which this occurs should be of the highest priority because once the data is formulated, we need to write it to the container, transfer it over the network, and read it on the other end as fast as possible.
Network transmission is currently being done using the low level WinSock RIO API and we're moving data from the application to the wire as fast as possible already. Transmission latency across the wire will always be a much higher and variable rate. The point at which we serialize our data before transmission is the next step in the chain to ensure we are wasting as little time as possible before getting our data out on the wire.
New packets will be received very quickly, and as such the ability to preallocate resources would be beneficial. For example:
Serializer DataHandler;
...
void NewIncomingPacket(const char* Data)
{
DataHandler.Reset();
DataHandler.Load(Data);
int x = DataHandler.Read();
float y = DataHandler.Read();
...
}
I'm looking for input from community experts on which direction to go here.
If you don't care about endianness and only want to serialize trivial types than a simple memcpy will be the fastest and also safe. Just memcpy into/out of the buffer when serializing/deserializing.
#include <iostream>
#include <vector>
#include <cstring>
#include <cstdint>
#include <type_traits>
#include <cstddef>
template <std::size_t CapacityV>
struct BinaryContainer
{
BinaryContainer() :
m_write(0),
m_read(0)
{
}
template <typename T>
void write(const std::vector<T>& vec)
{
static_assert(std::is_trivial_v<T>);
// TODO: check if access is valid
const std::size_t bytes = vec.size() * sizeof(T);
std::memcpy(m_buffer + m_write, vec.data(), bytes);
m_write += bytes;
}
template <typename T>
void write(T value)
{
static_assert(std::is_trivial_v<T>);
// TODO: check if access is valid
const std::size_t bytes = sizeof(T);
std::memcpy(m_buffer + m_write, &value, bytes);
m_write += bytes;
}
template <typename T>
std::vector<T> read(std::size_t count)
{
static_assert(std::is_trivial_v<T>);
// TODO: check if access is valid
std::vector<T> result;
result.resize(count);
const std::size_t bytes = count * sizeof(T);
std::memcpy(result.data(), m_buffer + m_read, bytes);
m_read += bytes;
return result;
}
template <typename T>
T read()
{
static_assert(std::is_trivial_v<T>);
// TODO: check if access is valid
T result;
const std::size_t bytes = sizeof(T);
std::memcpy(&result, m_buffer + m_read, bytes);
m_read += bytes;
return result;
}
const char* data() const
{
return m_buffer;
}
std::size_t size() const
{
return m_write;
}
private:
std::size_t m_write;
std::size_t m_read;
char m_buffer[CapacityV]; // or a dynamically sized equivalent
};
int main()
{
BinaryContainer<1024> cont;
{
std::vector<std::uint32_t> values = {1, 2, 3, 4, 5};
// probably want to make serializing size part of the vector serializer
cont.write(values.size());
cont.write(values);
}
{
auto size = cont.read<std::vector<std::uint32_t>::size_type>();
auto values = cont.read<std::uint32_t>(size);
for (auto val : values) std::cout << val << ' ';
}
}
Demo: http://coliru.stacked-crooked.com/a/4d176a41666dbad1
I've written seriously, an header-only fast C++ library that should do what you want :-)
It provides both a serializer and a de-serializer.
Serialized data is portable across different architectures and endianness. No external dependencies.
seriously::Packer<1024> packer; // a 1024 byte serialization buffer
int32_t value1 = 83656;
bool value2 = true;
int16_t value3 = -2345;
std::string value4("only an example");
double value5 = -6.736;
std::vector<int64_t> value6;
value6.push_back(42);
value6.push_back(11);
value6.push_back(93);
packer << value1 << value2 << value3 << value4 << value5 << value6;
std::cout << "packed size: " << packer.size() << std::endl;
// packer.data() contains the serialized data
int32_t restored1;
bool restored2;
int16_t restored3;
std::string restored4;
double restored5 = -6.736;
std::vector<int64_t> restored6;
packer >> restored1 >> restored2 >> restored3 >> restored4 >> restored5 >> restored6;
std::cout << "unpacked: " << restored1 << " " << (restored2 ? "t" : "f") << " " << restored3 << " " << restored4 << " " << restored5 << std::endl;
std::vector<int64_t>::const_iterator it;
for (it = restored6.begin(); it != restored6.end(); it++) {
std::cout << *it << std::endl;
}
I'm writing a periodic table program to help me understand classes.
I want to be able to display/sort the elements by several properties such as whether it's a metal, nonmetal, or metalloid. I'm not sure hwo to do it, but my first guess was to create an array of objects; however, I'm having problems using my constructor to set the values.
Class
class Element{
public:
enum class groupNames { HYDROGEN, ALKALI, ALKALINE, GROUP_THREE, GROUP_FOUR, GROUP_FIVE,
GROUP_SIX, GROUP_SEVEN, GROUP_EIGHT, GROUP_NINE, GROUP_TEN,
GROUP_ELEVEN,GROUP_TWELVE, GROUP_THIRTEEN, GROUP_FOURTEEN,
GROUP_FIFTEEN, CHALCOGEN, HALOGEN, NOBLE_GAS
};
enum class orbitals {ORBITAL_NOTSET, S_BLOCK, P_BLOCK, D_BLOCK, F_BLOCK};
enum class metal_status {METAL = 0, METALLOID, NONMETAL};
Element();
Element(int aNumber, int pNumber,groupNames groupnames, metal_status MetalStatus, orbitals Orbital,std::string eName, std::string eSybol);
void displayProperties();
private:
groupNames groupNumber;
orbitals orbital;
metal_status metalStatus;
std::string elementSymbol;
std::string elementName;
int atomicNumber;
int periodNumber;
};
Element::Element()
{
atomicNumber = 0;
periodNumber = 0;
groupNumber = groupNames::HYDROGEN;
metalStatus = metal_status::METAL;
orbital = orbitals::ORBITAL_NOTSET;
elementName = "NULL";
elementSymbol = "NULL";
}
Element::Element(int aNumber, int pNumber,groupNames groupnames, metal_status MetalStatus, orbitals Orbital,std::string eName, std::string eSymbol)
{
groupNumber = groupnames;
metalStatus = MetalStatus;
orbital = Orbital;
atomicNumber = aNumber;
periodNumber = pNumber;
elementName = eName;
elementSymbol = eSymbol;
}
void Element::displayProperties()
{
std::cout << elementName << ", " << elementSymbol << "\n"
<< "Group Number: " << as_integer(groupNumber) << "\n"
<< "Metal Status: " << as_integer(metalStatus) << "\n"
<< "Orbital: " << as_integer(orbital) << "\n"
<< "Atomic Number: "<< atomicNumber << "\n"
<< "Period Number: "<< periodNumber;
}
Previous Method of Initialization //Works fine, the problem is I can't sort by properties
Element Hydrogen(1,1, Element::groupNames::HYDROGEN, Element::metal_status::NONMETAL, Element::orbitals::S_BLOCK, "Hydrogen", "H");
Element Helium(2, 1, Element::groupNames::NOBLE_GAS, Element::metal_status::NONMETAL, Element::orbitals::S_BLOCK, "Helium", "He");
std::array Method -- Problem!
std::array<Element, 115> Elements =
{
Elements[0],
Elements[1](1,1, Element::groupNames::HYDROGEN, Element::metal_status::NONMETAL, Element::orbitals::S_BLOCK, "Hydrogen", "H")
};
Error: error: no match for call to '(std::array::value_type {aka Element}) (int, int, Element::groupNames,
Element::metal_status, Element::orbitals, const char [9], const char
[2])'
You may need two sets of braces (I was pulling my hair out figuring out what clang was complaining about.) I suggest uniform initialization. Also, I prefixed your enums with Element for qualification and changed them to match what their names are in your class definition.
std::array<Element, 115> Elements =
{{
{},
{1,1, Element::groupNames::HYDROGEN, Element::metal_status::NONMETAL, Element::orbitals::S_ORBITAL, "Hydrogen", "H"}
}};
Alternatively, you can try:
std::array<Element, 115> Elements
{
Element(),
Element(1,1, Element::groupNames::HYDROGEN, Element::metal_status::NONMETAL, Element::orbitals::S_ORBITAL, "Hydrogen", "H")
};
I'm looking into C++11 move constructors but something doesn't work. In fact the issue is even before I started writing such a constructor. Here's a code snipped:
#include <iostream>
#include <string>
#include <sstream>
class Object {
static std::ostream& log(Object &obj) {
std::cout << "Object::id = " << obj.mId << "::";
return std::cout;
}
unsigned mId = 0;
std::string *mText = nullptr;
unsigned nextUniqueId() const {
static unsigned id = 0;
return ++id;
}
const std::string textInfo() const {
std::ostringstream oss;
oss << "mText # " << &mText;
if (mText) oss << " = " << *mText;
return oss.str();
}
public:
Object() = delete;
Object& operator= (const Object&) = delete;
explicit Object(const std::string& str) : mId(this->nextUniqueId()), mText(new std::string(str)) {
Object::log(*this) << "constructor::one-argument\n";
}
Object(const Object& obj) : mId(this->nextUniqueId()), mText(new std::string(*obj.mText)) {
Object::log(*this) << "constructor::copy\n";
}
virtual ~Object() {
Object::log(*this) << "destructor::" << this->textInfo() << "\n";
if (mText) {
delete mText;
mText = nullptr;
}
}
};
static Object get_object() {
return Object("random text");
}
int main(int argc, char **argv) {
Object a("first object"); // OK
/*
* Expected behaviour: inside get_object() function new Object is created which is then copied into
* variable b. So that new ID should be given.
*/
Object b = get_object(); // What the hell?! Not what expected! Why?
std::cout << std::endl;
return 0;
}
The expected output is similiar to this:
Object::id = 1::constructor::one-argument
Object::id = 2::constructor::one-argument
Object::id = 2::destructor::mText # 0x7fff32c25f70 = random text
Object::id = 3::constructor::copy
Object::id = 3::destructor::mText # <DIFFERENT THAN IN ID=2> = random text
Object::id = 1::destructor::mText # 0x7fff32c25f90 = first object
I get this instead:
Object::id = 1::constructor::one-argument
Object::id = 2::constructor::one-argument
Object::id = 2::destructor::mText # 0x7fff32c25f70 = random text
Object::id = 1::destructor::mText # 0x7fff32c25f90 = first object
which looks like variable b is created on spot (something like inline maybe?). Frankly speaking I don't know what's going on, can anyone explain?
The compiler optimized out the copy/move is all...
That is called return value optimization or RVO. The compiler opted to create the temporary returned by get_object() directly in the memory location of b in main. It is sanctioned by the standard and a very common optimization.
The compiler is allowed to apply "return value optimization" RVO and that's why the copy is optimized out. Note that the standard allows this despite the side effects related to the output message
My program reads user's input and create easy "table". User on the start specifies data types of columns and the number of rows.
User's input:
create table
add attribute string Name
add attribute int Age
rows 3
I need prepare a structure from user's input now. I have something like this:
CTable
{
unsigned attributesCnt;
string * attributesNames;
void ** attributes;
};
So, from the user's input, the progam does these steps:
CTable myTable;
myTable.attributesCnt = 2; // string "Name", int "Age"
myTable.attributesNames = new string[2];
myTable.attributesNames[0] = "Name";
myTable.attributesNames[1] = "Age";
attributes = new void[2]; // 2 attributes
attributes[0] = (void*) new string[3]; // there will be 3 rows
attributes[1] = (void*) new int[3];
I need remember that "attributes[0]" is string and "attributes[1]" is int too.
Is this "right" way?
I would like use only standard libraries.
What you are looking for is a tagged union also called a variant. It allows you to store multiple data types at the same location just like a regular union but includes an additional but separate data member that indicates it's type. The C++ Standard Library does not include variants but they are easy enough to implement.
Once you have a variant you can apply it to your example like below.
myTable.attributesNames[0] = "Name";
myTable.attributesNames[1] = "Age";
// I recommend using std::vector here instead of using new/delete yourself
attributes = new Variant*[2]; // 2 attributes
attributes[0] = new Variant("player name");
attributes[1] = new Variant(player_age);
The following example shows how the variant might be implemented.
struct Variant
{
enum Type
{
INT,
STRINGPTR
};
Type type_;
union
{
int int_;
const char* stringptr_;
} data_;
explicit Variant(int data) : type_(INT)
{
data_.int_ = data;
}
explicit Variant(const char *data) : type_(STRINGPTR)
{
data_.stringptr_ = data;
}
Type getType() const { return type_; }
int getIntValue() const
{
if(type_ != INT)
throw std::runtime_error("Variant is not an int");
return data_.int_;
}
const char *getStringPtr() const
{
if(type_ != STRINGPTR)
throw std::runtime_error("Variane is not a string");
return data_.stringptr_;
}
};
int main()
{
Variant intval(1);
Variant stringval("hello");
std::cout << intval.getIntValue() << std::endl;
std::cout << stringval.getStringPtr() << std::endl;
}