reinterpret_cast between std::unordered_map - c++

I have the following unordered_maps:
struct a{
std::string b;
};
int main()
{
std::unordered_map<std::string, std::string> source;
source["1"] = "Test";
source["2"] = "Test2";
std::unordered_map<std::string, a> dest = *reinterpret_cast<std::unordered_map<std::string, a>*>(&source);
std::cout << dest["1"].b << std::endl;
std::cout << dest["2"].b << std::endl;
}
Using a reinterpret_cast I convert source into dest. This works since struct a only consists of a std::string.
My question: Is this actually good pratice? GCC yields the following warning:
dereferencing type-punned pointer will break strict-aliasing rules
Can I safely ignore this? Or is there any potential drawback of just casting the raw bytes of STL container?
(cpp.sh/5r2rh)

No that is not good practice. Your code is not safe. In fact it's the opposite: undefined behavior, meaning sometimes it works sometimes it won't, even without telling you.
The real problem is that you have no "legal" way to convert a std::string to an struct a. This isn't C, don't use stuff as plain bytes, use the type system of the language. Then the compiler will help you do avoid bad mistakes.
This is my solution:
#include <unordered_map>
#include <string>
#include <iostream>
#include <algorithm>
struct a {
std::string b;
a () = default;
a (const std::string& b) : b(b){}
};
int main() {
std::unordered_map<std::string, std::string> source;
source["1"] = "Test";
source["2"] = "Test2";
std::unordered_map<std::string, a> dest;
std::transform(source.cbegin(),source.cend(),std::inserter(dest,dest.end()),[](const auto& value)
{
return std::forward_as_tuple(value.first,value.second);
});
std::cout << dest["1"].b << std::endl;
std::cout << dest["2"].b << std::endl;
}
If you have performance concerns, you can also add a move constructor and more, but trust me, readable clean code, is fast code. Otherwise the bootle neck is not that non casting code, but the use of maps, copying instead of moving and other stuff. But don't optimize prematurely.

Related

Initialize a string with a char pointer with zero-copy

Say I am given a long and null-terminated cstring as char* text_ptr. I own text_ptr and I am responsible of free()ing it. Currently, I use text_ptr and free() it each time after use.
I try to improve memory safety a bit by wrapping it in a C++ class so that I can enjoy the benefit of RAII. There could be many ways to achieve it. A naive way is: string text_ptr(text_ptr);. However, by doing so, memory is copied once and I still need to manually free() my text_ptr. It would be better if I can avoid memory copy and free() (as this text_ptr is created frequently, performance could take a big hit if I copy it each time). My current thought:
Is it possible to transfer the ownership of text_ptr to a string text_str? Hypothetically, I do text_str.data() = text_ptr;.
Thanks
std::string can't receive ownership of an external buffer. The best you can do is std::unique_ptr.
By default std::unique_ptr will use delete (or delete[]), but you need std::free(), so a custom deleter is required:
#include <cstdlib>
#include <memory>
struct FreeDeleter
{
void operator()(void *p) const
{
std::free(p);
}
};
int main()
{
std::unique_ptr<char[], FreeDeleter> ptr((char *)malloc(42));
}
If you also store the length, you can construct a temporary std::string_view from pointer+length when needed, to conveniently read the string.
Or, a oneliner: std::unique_ptr<char[], std::integral_constant<void(*)(void *), std::free>>.
Another one for C++20: std::unique_ptr<char[], decltype([](void *p){std::free(p);})>.
An idea (not sure it’s a good one, tho)
#include <iostream>
#include <string_view>
#include <cstring>
#include <memory>
struct my_string_view : public std::string_view
{
using std::string_view::string_view;
std::shared_ptr<char[]> _data;
explicit my_string_view( char * data )
: std::string_view(data)
, _data{data, free}
{ }
};
void f( const my_string_view & s )
{
std::cout << "f: \"" << s << "\"\n";
}
int main()
{
my_string_view s( strdup( "Hello world!" ) );
f( s );
std::cout << "main: \"" << s << "\"\n";
}
(This version requires C++17. For older versions of the standard you’ll have to specify the default_deleter<char[]>() explicitly.)

How to solve the crash when smart pointer uses the structure in the class?

#include <iostream>
#include <memory>
class CTest {
public:
struct StructTest {
std::string StrTest;
};
};
int main() {
std::shared_ptr<CTest::StructTest> opt;
opt->StrTest = "hello world\n";
std::cout << opt->StrTest;
return 0;
}
This code is so simple, but it crashes. I can't understand why smart pointer can't use it like this.
The problem is, that opt is a pointer, but you do not initialize it to point to anything. It's a good habit to use auto to initialize smart pointers:
auto opt = std::make_shared<CTest::StructTest>();
Using auto like this ensures that you don't forget to initialize the pointer, because auto opt; will just not compile with modern C++, and you will immediately see it won't make any sense either.
opt is just default-constructed, it points to nothing. Dereference on it leads to UB.
You can change the code to
std::shared_ptr<CTest::StructTest> opt = std::make_shared<CTest::StructTest>();

Why this code does allow to push_back unique_ptr do vector?

so I thought adding unique to vector shouldn't work.
Why does it work for the below code?
Is it cause by not setting copy ctor as "deleted"??
#include <iostream>
#include <vector>
#include <memory>
class Test
{
public:
int i = 5;
};
int main()
{
std::vector<std::unique_ptr<Test>> tests;
tests.push_back(std::make_unique<Test>());
for (auto &test : tests)
{
std::cout << test->i << std::endl;
}
for (auto &test : tests)
{
std::cout << test->i << std::endl;
}
}
There is no copy here, only moves.
In this context, make_unique will produce an instance of unique pointer which is not named, and this push_back sees it as a r-value reference, which it can use as it wants.
It produce pretty much the same result than this code would:
std::vector<std::unique_ptr<Test>> tests;
auto ptr = std::make_unique<Test>();
tests.push_back(std::move(ptr));
This is called move semantics if you want to search more info on the matter. (and this only works from c++11 and beyond)
There are two overloads of std::vector::push_back according to https://en.cppreference.com/w/cpp/container/vector/push_back
In your case you will use the one with rvalue-ref so no copying required.

Does std::optional forwards rvalueness when contained object functions are called?

Little known feature of C++ is ref-qualifiers for member functions.
It works as I expect it to work in most cases, but it seems that std::optional does not forward the knowledge of its imminent demise to contained object member functions.
For example consider the following code:
#include <chrono>
#include <iostream>
#include <optional>
struct Noisy {
Noisy(const std::string& data): data_(data){
}
~Noisy(){
std::cout << "Goodbye" << std::endl;
}
std::string data_;
const std::string& data() const & {
std::cout << "returning data by ref" << std::endl;
return data_;
}
std::string data() && {
std::cout << "returning data by move" << std::endl;
return std::move(data_);
}
};
int main() {
for (const auto chr: Noisy{"Heeeeeeeeeeeeeeeeello wooooorld"}.data()){
std::cout << chr;
}
std::cout << std::endl;
for (const auto chr: std::optional<Noisy>{"Heeeeeeeeeeeeeeeeello wooooorld"}->data()){
std::cout << chr;
}
std::cout << std::endl;
}
output is:
returning data by move
Goodbye
Heeeeeeeeeeeeeeeeello wooooorld
returning data by ref
Goodbye
(crash in clang with sanitizer or garbage(UB))
I was hoping that temporary std::optional will be kind enough to call correct (data() &&) function, but it seems it does not happen.
Is this a language limitation, or std::optional just does not have correct machinery for this?
Full godbolt link.
note: my motivation is hacking around to see if I can be clever to enable safer usage of my classes in range based for loop, but realistically it is not worth the effort, this question is mostly about learning about language.
Overloaded operator arrow cannot do what you want; it terminates with a pointer always.
x->y is defined by the standard as (*x).y if and only if x is a pointer; otherwise it is (x.operator->())->y. This recursion only terminates if you hit a pointer.1
And there is no pointer to temporary type. Try this:
const auto chr: (*std::optional<Noisy>{"Heeeeeeeeeeeeeeeeello wooooorld"}).data()
Which does call the rvalue method. (via #largest_prime).
1 This recursion can also do Turing complete computation.

Life time of a c++ static_cast result

I wonder - what are cast result in cpp actualy is? And specificly - what are their lifetime?
Consider this example:
#include <iostream>
#include <stdint.h>
using namespace std;
class Foo
{
public:
Foo(const int8_t & ref)
: _ptr(&ref)
{}
const int8_t & getRef() { return *_ptr; }
private:
const int8_t * _ptr;
};
enum Bar
{
SOME_BAR = 100
};
int main()
{
{
int32_t value = 50;
Foo x(static_cast<int16_t>(value));
std::cout << "casted from int32_t " << x.getRef() << std::endl;
}
{
Bar value = SOME_BAR;
Foo x(static_cast<int16_t>(value));
std::cout << "casted from enum " << x.getRef() << std::endl;
}
return 0;
}
Output:
casted from int32_t 50
casted from enum 100
It works - but is is safe? With integers i can imagine that compiller somehow cast a "pointer" to needed part of target variable bytes. But what happens when you cast int to float?
static_cast creates an rvalue that exists for the life of the expression. That is, up until the semi-colon. See Value Categories. If you need to pass a reference to the value, the compiler will put the value on the stack and pass that address. Otherwise, it will probably stay in a register, especially with optimizations turned on.
The way you are using it, at the place you're using it, static_cast is completely safe. In the Foo class however, you are saving a pointer to the rvalue. It is only luck that the program executes correctly. A more complex example will probably reuse those stack locations for other uses.
Edited to elaborate on safety of static_cast.