Insertion into map<char*, struct _channel*l> channel - c++

I have this map:
typedef struct _channel {
int stuffs;
}channel;
map<char*, struct _channel*> channel
But when I use:
char* str = (char*)malloc(MAX_NAME);
channel* pointer = (channel*)malloc(sizeof(channel));
channel[str] = pointer;
I don't get pass through it:
// This should've been true, even with find() method
if(channel[str] != 0) {
// code here
}

The default equality comparison for any pointer, including char*, is just to literally compare the pointer.
If you have two C-strings, at different locations in memory, with the same contents, comparing pointers to them will not give you the answer you wanted. That's why we use strcmp instead when comparing these things.
It's possible to make std::map do that, so that lookup works as you intended, but instead you should just use std::string as a key; it does this stuff for you.
As an aside, you're using a lot of C-isms here. typedef struct, malloc… Also, you can't create an object with malloc before C++20, so your program technically has undefined behaviour, though this was more a standard oversight than anything any implementation ever deliberately took advantage of, so in practice you're likely to be fine (ref). Robust, safe, well-defined code is recommended regardless.
#include <map>
#include <string>
#include <memory>
struct channel
{
int stuff;
};
std::map<std::string, std::unique_ptr<channel>> channels;
int main()
{
channel["key"] = std::make_unique<channel>();
// ...
if (channel["key"] != nullptr)
{
// ...
}
}
You probably don't need the pointer indirection at all; you can likely just store channels in the map as values.
If not, because code elsewhere needs to "own" the channels too, std::shared_ptr is your friend.

Related

Why can't we change data pointer of std::vector?

I have an array char* source and a vector std::vector<char> target. I'd like to make the vector target point to source in O(1), without copying the data.
Something along these lines:
#include <vector>
char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);
target.setData(source); // <- Doesn't exist
// OR
std::swap(target.data(), source); // <- swap() does not support char*
delete[] source;
Why is it not possible to manually change where a vector points to? Is there some specific, unmanageable problem that would arise if this was possible?
C++ vector class supports adding and deleting elements, with guaranteed consecutive order in memory. If you could initialize your vector with existing memory buffer, and add enough elements to it, it would either overflow or require reallocation.
The interface of vector assumes that it manages its internal buffer, that is, it can allocate, deallocate, resize it whenever it wants (within spec, of course). If you need something that is not allowed to manage its buffer, you cannot use vector - use a different data structure or write one yourself.
You can create a vector object by copying your data (using a constructor with two pointers or assign), but this is obviously not what you want.
Alternatively, you can use string_view, which looks almost or maybe exactly what you need.
std::vector is considered to be the owner of the underlying buffer. You can change the buffer but this change causes allocation i.e. making a copy of the source buffer which you don't want (as stated in the question).
You could do the following:
#include <vector>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);
target.assign(source, source + 3);
delete[] source;
return 0;
}
but again std::vector::assign:
Replaces the contents with copies of those in the range [first, last).
So copy is performed again. You can't get away from it while using std::vector.
If you don't want to copy data, then you should use std::span from C++20 (or create your own span) or use std::string_view (which looks suitable for you since you have an array of chars).
1st option: Using std::string_view
Since you are limited to C++17, std::string_view might be perfect for you. It constructs a view of the first 3 characters of the character array starting with the element pointed by source.
#include <iostream>
#include <string_view>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::string_view strv( source, 3 );
delete[] source;
return 0;
}
2nd option: Using std::span from C++20
std::span comes from C++20 so it might not be the most perfect way for you, but you might be interested in what it is and how it works. You can think of std::span as a bit generalized version of std::string_view because it is a contiguous sequence of objects of any type, not just characters. The usage is similar as with the std::string_view:
#include <span>
#include <iostream>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::span s( source, 3 );
delete[] source;
return 0;
}
3rd option: Your own span
If you are limited to C++17, you can think of creating your own span struct. It might still be an overkill but let me show you (btw take a look at this more elaborated answer):
template<typename T>
class span {
T* ptr_;
std::size_t len_;
public:
span(T* ptr, std::size_t len) noexcept
: ptr_{ptr}, len_{len}
{}
T& operator[](int i) noexcept {
return *ptr_[i];
}
T const& operator[](int i) const noexcept {
return *ptr_[i];
}
std::size_t size() const noexcept {
return len_;
}
T* begin() noexcept {
return ptr_;
}
T* end() noexcept {
return ptr_ + len_;
}
};
int main() {
char* source = new char[3] { 1, 2, 3 };
span s( source, 3 );
delete[] source;
return 0;
}
So the usage is the same as with the C++20's version of std::span.
The std::string_view and std::span are good things to have (if you have compiler version supporting them). Rolling your own similars is ok too.
But some people miss the whole point why one would want to do this exactly to a vector:
Because you have an API that gives Struct[] + size_t and give you ownership
and you also have an API that accepts std::vector<Struct>
ownership could be easily transferred into the vector and no copies made!
You can say: But what about custom allocators, memory mapped file pointers, rom memory that I could then set as the pointer?
If you are already about to set vector internals you should know what you are doing.
You can try to supply a "correct" allocator in those cases to your vector actually.
Maybe give a warning on compiling this code yes, but it would be nice if it would be possible.
I would do it this way:
std::vector would get a constructor that asks for a std::vector::raw_source
std::vector::raw_source is an uint8_t*, size_t, bool struct (for now)
bool takeOwnership: tells if we are taking ownership (false => copy)
size_t size: the size of the raw data
uint8_t* ptr: the pointer to raw data
When taking ownership, vector resize and such uses the vectors allocation strategy as you otherwise provided with your template params anyways - nothing new here. If that does not fit the data you are doing wrong stuff!
Yes API I say look more complicated than a single "set_data(..)" or "own_memory(...)" function, but tried to make it clear that anyone who ever uses this api pretty much understands implications of it automagically. I would like this API to exists, but maybe I still overlook other causes of issues?

How can I build and use a vector(map(pair(struct))) in C++?

I want to build a variable like vector(map(pair(struct))) and use it to store information in C++, I try to use following code:
struct st_Base
{
char Type[2];
double Price;
queue<double> Samples;
};
vector< map< string, pair< st_Base, st_Base >* >* > gv_combo;
string str_source = "test123";
gv_combo.push_back(new map<str_source, new pair<st_Base, st_Base>>);
But when I run the program, it always show me lots of errors. Can anyone told me the right way to build it, place data in it, and read it?
Consider not using dynamic allocation via new keyword (Manual memory management is prone to errors). If your memory needs to be allocated dynamically use unique pointer std::unique_ptr.
What you are esentially creating is a container holding a pointer to container that is holding a pair of values (string (key), and pointer to pair of structs (value)).
#include <vector>
#include <map>
#include <utility>
#include <memory>
#include <iostream>
struct st_Base { int foo; };
int main()
{
typedef std::pair< st_Base, st_Base> weird_pair;
std::vector<std::map<std::string, weird_pair>> gv_combo;
string str_source = "test123";
weird_pair pair = make_pair(st_Base{ 10 }, st_Base{ 11 });
gv_combo.push_back(std::map<std::string, weird_pair>());
gv_combo.at(0).insert(std::pair<std::string, weird_pair>(str_source, pair));
std::cout << gv_combo.at(0).at("test123").second.foo;
return 1;
}
But this example is extremly unreadable (at least for me). Access to members of structs is not straightforwarwd (needs to call at() to localize element in map, then use first/second to access apropriate st_Base which results in ever increasing chain of calls.
Adding unique_ptr would result in even longer chain that would put my brain on a verge of scrapping the entire code after working with it for any period of time.
Notes to OP:
-read documentation carefully, it's your friend
-allocate with keyword new only when you really have to (eg. obscure framework pre c++11)
-typedefs save lifes
-pointers can get out of hand quickly if you don't wrap them into nice structure
-objects can use initializer lists {} to give them data during construction of the object. it's worth noting that C and C++ versions {} are not exchangable ( st_Base{.foo=10} is legal in C, but illegal in c++ )
This seems to be what you are trying to achieve:
struct st_Base {
char Type[2];
double Price;
std::queue<double> Samples;
};
std::vector<std::map<std::string, std::pair<st_Base, st_Base>>> gv_combo;
string str_source = "test123";
std::map<std::string, std::pair<st_Base, st_Base>> my_map;
my_map[str_source] = std::make_pair(st_Base(...), st_Base(...)); // instert pair of objects here
gv_combo.push_back(my_map);

C++ map<std::string> vs map<char *> performance (I know, "again?")

I was using a map with a std::string key and while everything was working fine I wasn't getting the performance I expected. I searched for places to optimize and improved things only a little and that's when a colleague said, "that string key is going to be slow."
I read dozens of questions and they consistently say:
"don't use a char * as a key"
"std::string keys are never your bottleneck"
"the performance difference between a char * and a
std::string is a myth."
I reluctantly tried a char * key and there was a difference, a big difference.
I boiled the problem down to a simple example:
#include <stdio.h>
#include <stdlib.h>
#include <map>
#ifdef USE_STRING
#include <string>
typedef std::map<std::string, int> Map;
#else
#include <string.h>
struct char_cmp {
bool operator () (const char *a,const char *b) const
{
return strcmp(a,b)<0;
}
};
typedef std::map<const char *, int, char_cmp> Map;
#endif
Map m;
bool test(const char *s)
{
Map::iterator it = m.find(s);
return it != m.end();
}
int main(int argc, char *argv[])
{
m.insert( Map::value_type("hello", 42) );
const int lcount = atoi(argv[1]);
for (int i=0 ; i<lcount ; i++) test("hello");
}
First the std::string version:
$ g++ -O3 -o test test.cpp -DUSE_STRING
$ time ./test 20000000
real 0m1.893s
Next the 'char *' version:
g++ -O3 -o test test.cpp
$ time ./test 20000000
real 0m0.465s
That's a pretty big performance difference and about the same difference I see in my larger program.
Using a char * key is a pain to handle freeing the key and just doesn't feel right. C++ experts what am I missing? Any thoughts or suggestions?
You are using a const char * as a lookup key for find(). For the map containing const char* this is the correct type that find expects and the lookup can be done directly.
The map containing std::string expects the parameter of find() to be a std::string, so in this case the const char* first has to be converted to a std::string. This is probably the difference you are seeing.
As sth noted, the issue is one of specifications of the associative containers (sets and maps), in that their member search methods always force a conversion to the key_type, even if an operator< exists that would accept to compare your key against the keys in the map despite their different types.
On the other hand, the functions in <algorithm> do not suffer from this, for example lower_bound is defined as:
template< class ForwardIt, class T >
ForwardIt lower_bound( ForwardIt first, ForwardIt last, const T& value );
template< class ForwardIt, class T, class Compare >
ForwardIt lower_bound( ForwardIt first, ForwardIt last, const T& value, Compare comp );
So, an alternative could be:
std::vector< std::pair< std::string, int > >
And then you could do:
std::lower_bound(vec.begin(), vec.end(), std::make_pair("hello", 0), CompareFirst{})
Where CompareFirst is defined as:
struct CompareFirst {
template <typename T, typename U>
bool operator()(T const& t, U const& u) const { return t.first < u.first; }
};
Or even build a completely custom comparator (but it's a bit harder).
A vector of pair is generally more efficient in read-heavy loads, so it's really to store a configuration for example.
I do advise to provide methods to wrap the accesses. lower_bound is pretty low-level.
If your in C++ 11, the copy constructor is not called unless the string is changed. Because std::string is a C++ construct, at least 1 dereference is needed to get at the string data.
My guess would be the time is taken up in an extra dereference (which if done 10000 times is costly), and std::string is likely doing appropriate null pointer checks, which again eats up cycles.
Store the std::string as a pointer and then you lose the copy constructor overhead.
But after you have to remember to handle the deletes.
The reason std::string is slow is that is constructs itself. Calls the copy constructor, and then at the end calls delete. If you create the string on the heap you lose the copy construction.
After compilation the 2 "Hello" string literals will have the same memory address. On the char * case you use this memory addresses as keys.
In the string case every "Hello"s will be converted to a different object. This is a small part (really really small) of your performance difference.
A bigger part can be that as all the "Hello"s you are using has the same memory address strcmp will always get 2 equivalent char pointers and I'm quite sure that it early checks for this case :) So it will never really iterate on the all characters but the std::string comparison will.
One solution to this is use a custom key class that acts as a cross between a const char * and a std::string, but has a boolean to tell at run time if it is "owning" or "non-owning". That way you can insert a key into the map which owns it's data (and will free it on destruction), and then compare with a key that does not own it's data. (This is a similar concept to the rust Cow<'a, str> type).
The below example also inherits from boost's string_ref to avoid having to re-implement hash functions etc.
NOTE this has the dangerous effect that if you accidentally insert into the map with the non-owning version, and the string you are pointing at goes out of scope, the key will point at already freed memory. The non-owning version can only be used for lookups.
#include <iostream>
#include <map>
#include <cstring>
#include <boost/utility/string_ref.hpp>
class MaybeOwned: public boost::string_ref {
public:
// owning constructor, takes a std::string and copies the data
// deletes it's copy on destruction
MaybeOwned(const std::string& string):
boost::string_ref(
(char *)malloc(string.size() * sizeof(char)),
string.size()
),
owned(true)
{
memcpy((void *)data(), (void *)string.data(), string.size());
}
// non-owning constructor, takes a string ref and points to the same data
// does not delete it's data on destruction
MaybeOwned(boost::string_ref string):
boost::string_ref(string),
owned(false)
{
}
// non-owning constructor, takes a c string and points to the same data
// does not delete it's data on destruction
MaybeOwned(const char * string):
boost::string_ref(string),
owned(false)
{
}
// move constructor, tells source that it no longer owns the data if it did
// to avoid double free
MaybeOwned(MaybeOwned&& other):
boost::string_ref(other),
owned(other.owned)
{
other.owned = false;
}
// I was to lazy to write a proper copy constructor
// (it would need to malloc and memcpy again if it owned the data)
MaybeOwned(const MaybeOwned& other) = delete;
// free owned data if it has any
~MaybeOwned() {
if (owned) {
free((void *)data());
}
}
private:
bool owned;
};
int main()
{
std::map<MaybeOwned, std::string> map;
map.emplace(std::string("key"), "value");
map["key"] += " here";
std::cout << map["key"] << "\n";
}

unordered_set: is pointer address a good hash?

i want to store a set of (smart) pointers in a hash set, either <boost/unordered_set>. After 10 seconds of thought, i came up with this hash function:
typedef boost::shared_ptr<myType> ref_t;
struct SharedPtrHash : public std::unary_function<ref_t, std::size_t> {
std::size_t operator()(ref_t const& obj) const {
return reinterpret_cast<std::size_t>( obj.get() );
}
};
My question is: is this hash a good idea? i'm entertaining the thought that this hash will have zero or very few collisions (maybe there is some prime-number modulus under the hood spoiling all my fun).
Further Details on purpose: The purpose of the hash is for recycling storage of big objects, so i need a fast way to detect if a big object is already in the bin.
in case it is not, what would be an ideal hash for pointers, either smart or dumb ones?
If you want to detect objects that are not identical even though their contents might be equal, you have no choice but to use the address of the object in the hash. The only question is whether to use the address directly or to run it through a formula. Dividing by sizeof(mytype) would tighten up the holes in the distribution.
Edit: Here's an untested template implementation that should work with all shared_ptr types, along with an equal_to function to complete the requirements for std::unordered_set. Don't use this generic implementation if you have other objects that require a hash based on the value instead of the pointer.
template<typename T>
size_t hash(const std::shared_ptr<T> & ptr)
{
return ((size_t) ptr.get()) / sizeof(T);
}
template<typename T>
bool equal_to(const std::shared_ptr<T> & left, const std::shared_ptr<T> & right)
{
return left.get() == right.get();
}
The following code compiles perfectly (GCC 4.7, Boost 1.47):
#include <boost/unordered_set.hpp>
#include <boost/shared_ptr.hpp>
struct Foo { };
int main()
{
boost::unordered_set<boost::shared_ptr<int>> s;
boost::shared_ptr<int> pi(new int);
s.insert(pi);
boost::unordered_set<boost::shared_ptr<Foo>> t;
boost::shared_ptr<Foo> pf(new Foo);
t.insert(pf);
}
The default Boost.Hash hash function for integral types is the identity function, so I don't think doing the same for pointers is a bad idea. It would have the same collision ratio.

C++ sort method

I want to sort a vector using std::sort, but my sort method is a static method of a class, and I want to call std::sort outside it, but it seems to be trouble doing it this way.
On the class:
static int CompareIt(void *sol1, void *sol2) { ... }
std::sort call:
sort(distanceList.at(q).begin(),
distanceList.at(q).end(),
&DistanceNodeComparator::CompareIt);
Shouldn't it be possible to do this way?
std::sort takes a comparator that accepts value of the type held in the collection and returns bool. It should generally implement some notion of <. E.g., assuming your distanceList elements have collections of integers (I assume they don't, but for the sake of the example):
static bool CompareIt(int sol1, int sol2) { ... }
And of course you only need to supply a comparator if there isn't already a < operator that does the right thing for your scenario.
It should be a boolean method (sort uses operator <() by default to compare values)
The comparison function you've provided has the signature of the one needed by qsort, which is the sorting function that C provided before C++ came along. sort requires a completely different function.
For example if your declaration of distanceList is std::vector<DistanceNode> your function would look like:
static bool CompareIt(const DistanceNode &sol1, const DistanceNode &sol2)
{
return sol1.key < sol2.key;
}
Notice that sorting a std::list with the standard sort algorithm isn't efficient, which is why list supplies its own sort member function.
As others have mentioned, it needs a boolean return type. Here's an example which works:
#include "stdafx.h"
#include <vector>
#include <algorithm>
using namespace std;
class MyClass
{
public:
static bool CompareIt(const void *a1, const void *a2)
{
return a1 < a2;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
// Create a vector that contains elements of type MyData
vector<void*> myvector;
// Add data to the vector
myvector.push_back((void*)0x00000005);
myvector.push_back((void*)0x00000001);
// Sort the vector
std::sort(myvector.begin(), myvector.end(), MyClass::CompareIt);
// Display some results
for( int i = 0; i < myvector.size(); i++ )
{
printf("%d = 0x%08X\n", i, myvector[i] );
}
return 0;
}
[Edit] Updated the code above to make it a little simpler. I'm not suggesting it's nice code, but without know more about the OPs real implementation, it's difficult to give a better example!
First, the return type should be bool. Actually the requirement is only that the return type be assignable to bool, which int is. But the fact that you're returning int suggests that you might have written a three-way comparator instead of the strict weak ordering required by std::sort.
Your CompareIt function takes two void* pointers as parameters. Is distanceList.at(q) a vector<void*> (or vector of something convertible to void*)? If not, then the comparator inputs aren't right either. Using void* with algorithms also suggests that you're doing something wrong, because much of the point of generic programming is that you don't need opaque pointers that later get cast back to their original type.