c++ stl selection for multiple keys - c++

c++ stl experts,
In a protocol stack implementation, I have a Message being sent from one layer to another. The source layer stores some information, and processes that information, on recepton of response from the second layer.
Now the information stored has 3 parameters which is used to compare the responses from the destination layer. (to get the correct one). i.e lets says session id, request number and infoID. The stored info contains a struture, lets say struct A.
which is the best way to implement this in the source layer to store info ?
Initially i thought of the following, as then there were only two keys
std::map<std::pair<u32, u32>, StructA> m_mSessionId2RNum2StructA;
But later requirement for another key. this got complicated
struct StructZ
{
u32 InfoId;
StructA stStructA;
};
std::map<std::pair<u32, u32>, StructZ> m_mSessionId2RNum2StructZ;
This doesnot look good. Any inputs/suggestions to improve this much appreciated
thanks
~pdk

Perhaps StructK can be a key for StructA as a value in a map:
struct StructK
{
u32 k1;
u32 k2;
u32 k3;
};
inline bool operator< (const StructK& lhs, const StructK& rhs)
{
if(lhs.k1 < rhs.k1)
return true;
else
if(lhs.k1 == rhs.k1)
{
if(lhs.k2 < rhs.k2)
return true;
else
if(lhs.k2 == rhs.k2)
{
return lhs.k3 < rhs.k3;
}
else
return false;
}
else
return false;
}
and then
map<StructK, StructA> myMap;
Of course, you can use any logic for operator<

std::pair is a special type of std::tuple which can only hold two values, whereas tuple can hold dozens (1). I.e. you seem to want std::tuple<u32, u32,u32> as a key now.
You get operator< for free with tuple, same as with pair.
If you need alternate indices, i.e. search by key 1, 2 or 3, then you need boost::multi_index containers.
(1) Check your implementation for precise limits.

Related

Tuples: No matching function for call to 'get'

I have 3 structs : Student, Citizen, Employee. I want user to be able to choose what struct they want to work with (std::vector of structs, actually). Since there's no way to define type at runtime, I created all 3 vectors, but will use only one of them (depending on the user's choice), others will stay empty:
std::vector<Student> container_student;
std::vector<Citizen> container_citizen;
std::vector<Employee> container_employee;
auto containers = make_tuple(container_student, container_citizen, container_employee);
std::cout << "Enter:\n0 to operate \"Student\" struct\n1 to operate \"Citizen\" struct\n2 to operate \"Employee\" struct\n";
std::cin >> container_type;
auto container = std::get<container_type>(containers);
But I get No matching function for call to 'get', even though container_type is an int and containers is a tuple.
Edit: understandable, auto can't make magic and I still try to make container's type to depend on runtime. But even if I try to use std::get<container_type>(containers) (probably define would help) instead of container in functions etc., I get the same error, which is not understandable.
Unfortunately, what you're proposing isn't possible in C++. The C++ typing and template system works at compile-time, where information read in from the user isn't available. As a result, anything passed into a template's angle braces needs to be determinable at compile-time. In your case, the number the user enters, indicating which option they want to select, is only knowable at runtime.
There are some routes you could take to achieve the same result, though. For example, one option would be to do something like this:
if (container_type == 0) {
auto container = std::get<0>(containers);
/* ... */
} else if (container_type == 1) {
auto container = std::get<1>(containers);
/* ... */
} /* etc */
Here, all the template angle braces are worked out at compile-time. (Then again, if this is what you're going to be doing, you wouldn't need the tuple at all. ^_^)
Another option would be to use templates, like this:
template <typename T> void doSomething(std::vector<T>& container) {
/* Put your code here */
}
/* Then, back in main... */
if (container_type == 0) {
doSomething(container_student);
} else if (container_type == 1) {
doSomething(container_citizen);
} /* etc */
This still requires you to insert some code to map from integer types to the functions you want to call, but it leaves you the freedom to have a container variable (the one in doSomething) that you can treat generically at that point.
It's basically the Fundamental Theorem of Software Engineering in action - all problems can be solved by adding another layer of indirection. :-)
Hope this helps!

How to neatly write two functions - one for checking if a solution exists, and another one for getting all solutions?

The obvious way is to just write two functions, but then they are almost identical. What I'm doing now is a function template with the return type (either bool or vector<something>) as the argument
template<typename ReturnType>
ReturnType foo(...){
constexpr bool return_bool = std::is_same<ReturnType, bool>::value;
ResultType results; //hopefully, the compiler takes it out in the bool case
And the plan is to use if constexpr(return_bool) when needed. But then I get this reoccurring piece of code
ReturnType result = foo<ResultType>(...);
if constexpr(return_bool){
if(result) return true;
}else std::copy(result.begin(), result.end(), std::back_inserter(results));
The return statement makes it hard to use standard anti-repetition techniques. I could use macros but then perhaps the repetition is better. Getting either all solutions or just the information whether one exists seems like a fairly general problem, is there a better way to do it?
I should've added that the function is performance-critical in the "does a solution exist?" case. That's why I want to have another version there and also why I don't want any costly abstractions.
You want two opposite features :
Reusing one solution in the other to avoid replication
Having an optimized version for solutionExists() to avoid a full result search
You didn't specify what is the solution your function returns, so I will explain why you can't have both using a simple example : your function is returning the number of ocurences of 0 in a vector of integers.
The function returning all solutions would look like this :
int GetNumberOfOccurencesOf0(const vector<int>& data)
{
int occurences = 0;
for (int i : data)
{
if (i == 0)
++occurences;
}
return occurences;
}
If you are not concerned about performance, your function for returning if there is a solution can be :
bool AreThereOccurencesOf0(const vector<int>& data)
{
return (GetNumberOfOccurencesOf0(data) > 0);
}
Note that there is no code duplication but the solution is not optimal : the data vector is iterated entirely. If you want an optimized solution, it would look like this :
bool AreThereOccurencesOf0(const vector<int>& data)
{
for (int i : data)
{
if (i == 0)
return true;
}
return false;
}
If your problem requires an optimized version of solutionExists(), you should write it and it should not need to reuse code from the getAllSolutions() function.

Constructing a tuple from values returned by member functions of objects inside another tuple

(This could be an XY Problem, so I'm providing some background information prior to the actual question.)
Background
I currently have a function (not a template) that computes different hash types (CRC32, MD5, SHA1, etc.) The data comes from a provider that can only provide a pointer to a chunk of the data at a time. The function computes the hashes on chunks of data iteratively.
Advancing to the next chunk is a very costly operation (involves decompression) and it can only go forward. Also the architecture must be kept zero-copy. As a result, all the selected hashes must be computed at once while iterating on the same chunks of data. Hash type selection is done through bool parameters:
std::tuple<uint32_t, QByteArray, QByteArray, QByteArray>
computeHashes(DataProvider& data, bool do_crc, bool do_md5, bool do_sha1,
bool do_sha256);
If one of the flags is false, the caller ignores the corresponding empty tuple element.
Actual Question
I am very unhappy with the above API. So I decided to write a cleaner looking function template. No boolean switches and no dummy tuple elements in the return value:
auto [crc, sha256] = computeHashes<Hash::CRC32, Hash::MD5>(data_provider);
I got the code mostly working, except for the last step where I need to actually return the results. This is trimmed down from the real code, and with only two hash functions in order to keep the example as short as possible:
enum class Hash { CRC32, MD5 };
template <HashType> struct Hasher
{};
template<> struct Hasher<HashType::CRC32>
{
void addData(const char* data, int len);
uint32_t result() const;
};
template<> struct Hasher<HashType::MD5>
{
void addData(const char* data, int len);
QByteArray result() const;
};
template <HashType... hash_types>
auto computeHashes(DataProvider& provider)
{
std::tuple<Hasher<hash_types>...> hashers;
while (provider.hasMoreChunks()) {
auto [chunk, len] = provider.nextChunk();
std::apply([chunk, len](auto&... hasher)
{ (..., hasher.addData(chunk, len); },
hashers);
}
return std::make_tuple( ??? );
}
I'm stuck at the last step: how do I return each result? A hard-coded return would look this:
return std::make_tuple(res, std::get<0>(hashers).result(),
std::get<1>(hashers).result());
This isn't suitable of course. How do I do this?
since std::apply forwards returned values by decltype(auto) you can just construct a tuple with std::apply and return it.
This can be coalesced with your transformations into one call.
template <HashType... hash_types>
static auto computeHashes(DataProvider& provider)
{
return std::apply(
[&provider](auto&&... hashers) {
while (provider.hasMoreChunks())
{
auto [chunk, len] = provider.nextChunk();
(..., hashers.addData(chunk, len));
}
return std::make_tuple(std::move(hashers.result())...);
},
std::tuple<Hasher<hash_types>...>{}
);
}

C++ map comparsion

Good morning, I'm stuck using a map in the correct way.
Situation
A database table with unique ID and two other codes
ID (long) | Type (long) | Name (string)
to fill the map correctly I've defined it in this way:
map<long, MyObject>
where key is my ID and the object holds all the stuff. The map works correctly, I load all rows and I navigate easily inside of it.
Troubles
Troubles come when I need to sort the rows using a criteria which is not the key but:
Type
Name
Looking around the Internet I found that I should:
Define the operator< for MyObject or...
Define another type of comparator for my map.
I did the step 1., but with no success (it is never called). I'm trying to do the point 2. but with even less success.
I'll paste some code to help:
class CSitoWebRigaVariante
{
public:
bool m_bSriDelete;
bool m_bSriVisibile;
long m_lSriId;
long m_lSriIdTipol;
long m_lSriCodGes;
CString m_strSriCodMat;
public:
CSitoWebRigaVariante(void);
CSitoWebRigaVariante(const CSitoWebRigaVariante& cRiga);
~CSitoWebRigaVariante(void);
bool operator<(const CSitoWebRigaVariante& cRiga);
void operator=(const CSitoWebRigaVariante& cRiga);
void Azzera(void);
static void CaricaDaMDB(CDB* pDB, long lIdVM, map<long, CSitoWebRigaVariante>& cRighe);
};
typedef map<long, CSitoWebRigaVariante> CSWRighe;
///> Static method to fill a map.
void CSitoWebRigaVariante::CaricaDaMDB(CADODatabase* pDB, long lIdVM, map<long, CSitoWebRigaVariante>& cRighe)
{
BOOL bValRit;
CRecordset* pRS;
CSitoWebRigaVariante riga;
CString strInt;
pRS = new CADORecordset(pDB);
strInt.Format(_T("SELECT * FROM SITOWEB_RIVARMAT WHERE sri_idvarmat = %ld;"), lIdVM);
cRighe.clear();
if (pRS->Open(strInt, CADORecordset::openQuery) == TRUE && pRS->GetRecordCount() > 0)
{
while (pRS->IsEOF() == FALSE)
{
bValRit = pRS->GetFieldValue(_T("sri_id"), riga.m_lSriId);
bValRit &= pRS->GetFieldValue(_T("sri_idtipol"), riga.m_lSriIdTipol);
bValRit &= pRS->GetFieldValue(_T("sri_codges"), riga.m_lSriCodGes);
bValRit &= pRS->GetFieldValue(_T("sri_codmat"), riga.m_strSriCodMat);
bValRit &= pRS->GetFieldValue(_T("sri_delete"), riga.m_bSriDelete);
bValRit &= pRS->GetFieldValue(_T("sri_visibile"), riga.m_bSriVisibile);
cRighe.insert(pair<long, CSitoWebRigaVariante>(riga.m_lSriCodGes, riga));
pRS->MoveNext();
}
}
pRS->Close();
delete pRS;
}
I'm using Visual Studio 2010, MFC.
Any help is appreciated.
std::map is not a multi-index associative container. Its find method (and other things) uses the key as a search criteria. There's no possibility to specify another search criteria. It's why it's a "single-index lookup table".
You can use Boost.MultiIndex. It was designed for your case and supports multiple indexes (as the name suggests), both unique and not-unique.
Or you can use multiple map instances with different keys. If keys are not unique you need std::multimap.
I would recommend you to use map for model (for storing data). When you need to display information, you can just output it in the order you need it to be shown. Sorting must be done not at level of storing items, but at level of displaying them.
Although, in each situation you will need to do reordering only once.
Also, if any help is appreciated, I would strongly recommend you to do a
typedef long MyId;
and to use VS 2015.
The map class provides a Compare parameter of the constructor. You could not complete your target by setting Compare as map has only support key compare function.
The first idea of mine is construct a class support your tables schema.
class Example
{
public:
<your code>
void sortByID();
void sortByType();
void sortByName();
private:
long ID_;
long Type_;
string Name_;
};
But it sounds terrible. As once your table change, you should hard copy.So why you just get result using database order by?

Problems with parsing a text header packet in c++

I am trying to parse header packet of SIP protocol (Similar to HTTP) which is a text based protocol.
The fields in the header do not have an order.
For ex: if there are 3 fields, f1, f2, and f3 they can come in any order any number of times say f3, f2 , f1, f1.
This is increasing the complexity of my parser since I don't know which will come first.
What should I do to overcome this complexity?
Ultimately, you simply need to decouple your processing from the order of receipt. To do that, have a loop that repeats while fields are encountered, and inside the loop determine which field type it is, then dispatch to the processing for that field type. If you can process the fields immediately great, but if you need to save the potentially multiple values given for a field type you might - for example - put them into a vector or even a shared multimap keyed on the field name or id.
Pseudo-code:
Field x;
while (x = get_next_field(input))
{
switch (x.type())
{
case Type1: field1_values.push_back(x.value()); break;
case Type2: field2 = x.value(); break; // just keep the last value seen...
default: throw std::runtime_error("unsupported field type");
}
}
// use the field1_values / field2 etc. variables....
Tony already gave the main idea, I'll get more specific.
The basic idea in parsing is that it is generally separated into several phases. In your case you need to separate the lexing part (extracting the tokens) from the semantic part (acting on them).
You can proceed in different fashions, since I prefer a structured approach, let us suppose that we have a simple struct reprensenting the header:
struct SipHeader {
int field1;
std::string field2;
std::vector<int> field3;
};
Now, we create a function that take a field name, its value, and fill the corresponding field of the SipHeader structure appropriately.
void parseField(std::string const& name, std::string const& value, SipHeader& sh) {
if (name == "Field1") {
sh.field1 = std::stoi(value);
return;
}
if (name == "Field2") {
sh.field2 = value;
return;
}
if (name == "Field3") {
// ...
return;
}
throw std::runtime_error("Unknown field");
}
And then you iterate over the lines of the header and for each line separate the name and the value and call this functions.
There are obviously refinements:
instead of a if-chain you can use a map of functors or you can fully tokenize the source and store the fields in a std::map<std::string, std::string>
you can use a state-machine technic to immediately act on it without copying
but the essential advice is the same:
To manage complexity you need to separate the task in orthogonal subtasks.