I need to write a program that output either to the std::cout or to some file. I was reading this post to see how to do. However I would like to separate the management of the ostream from the main. So I was thinking to write a class, but I'm a bit confused about the design. I have in mind two solution
(publicly) Subclass ostream: it this way I would have all method of ostream. However here the main problem would be the creator:
class sw_ostream : public ostream {
sw_ostream (cost char* filename) : ostream ( \\? ) {
\\ ...
}
\\...
}
because I should initialize ostream depending on filename, and apparently is impossible.
Create a class having osteram as a member and overload operator<<.
I'm sure that there are other, more elegant solution to this problem. Which design would you suggest?
I would try to split here the stream creation with the stream usage. std::ostream is already polymorphic, so as long as you pass a reference or pointer to the function that uses the stream, all good.
For the creation, I would go for creating the stream in the heap, as the post you linked to suggests. However, doing explicit memory management (raw new/delete) is dangerous, so I would use a smart pointer, like std::unique_ptr:
#include <fstream>
#include <memory>
struct ConditionalDeleter
{
bool must_delete;
void operator()(std::ostream* os) const { if (must_delete) delete os; }
};
using OstreamPtr = std::unique_ptr<std::ostream, ConditionalDeleter>;
OstreamPtr create_stream(bool to_file)
{
if (to_file)
return OstreamPtr { new std::ofstream {"myfile.txt"}, ConditionalDeleter {true} };
else
return OstreamPtr { &std::cout, ConditionalDeleter {false} };
}
void use_stream(std::ostream& os)
{
os << "Hello world!" << std::endl;
}
int main()
{
auto streamptr = create_stream(false);
use_stream(*streamptr);
}
I've used a custom deleter with std::unique_ptr. The reason for that is: if we are using the file, I want the stream to be deleted; but std::cout is a global object, which we must not delete. The agreement here is that when your OstreamPtr gets destroyed, ConditionalDeleter::operator() will get called. *streamptr returns you a reference to your std::ostream, which you can use as you want.
Please note you need C++11 support to use this solution.
Since they both inherit from std::ostream, you can just assign it to a std::ostream&.
In your case, you can simply do something like this:
#include <iostream>
#include <fstream>
void do_stuff(const char* filename = nullptr) {
std::ofstream _f;
std::ostream& os = filename ? (_f.open(filename), _f) : std::cout;
os << "Output normally";
// If you want to check if it is a file somewhere else
if (std::ofstream* fp = dynamic_cast<std::ofstream*>(&os)) {
std::ofstream& f = *fp;
// But here you can probably check the condition used to make the file
// (e.g. here `filename != nullptr`)
}
// After returning, `os` is invalid because `_f` dies, so you can't return it.
}
A simpler approach would be to not worry about this at all. Just put all of your code that outputs stuff inside one function that takes a std::ostream& parameter, and call it with a std::ofstream or another std::ostream:
void do_stuff(std::ostream& os) {
os << "Write string\n";
}
int main() {
if (using_file) {
std::ofstream f("filename");
do_stuff(f);
} else {
do_stuff(std::cout);
}
}
If you want to be able to return the object without the file closing and becoming a dangling reference, you need to store it somewhere. This example stores it in a struct:
#include <iostream>
#include <fstream>
#include <utility>
#include <new>
#include <cassert>
struct sw_ostream {
private:
// std::optional<std::fstream> f;
// Use raw storage and placement new pre-C++17 instead of std::optional
alignas(std::fstream) unsigned char f[sizeof(std::fstream)];
std::ostream* os;
bool did_construct_fstream() const noexcept {
// If `os` is a pointer to `f`, we placement new`d, so we need to destruct it
return reinterpret_cast<unsigned char*>(os) == f;
}
// Destroys currently held std::fstream
// (Must have been constructed first and have `os` point to it)
void destruct() noexcept {
static_cast<std::fstream&>(*os).~basic_fstream();
}
public:
sw_ostream() = default;
sw_ostream(std::ostream& os_) : os(&os_) {}
template<class... Args>
explicit sw_ostream(Args&&... args) {
os = new (f) std::fstream(std::forward<Args>(args)...);
}
sw_ostream(std::fstream&& f) : os(nullptr) {
*this = std::move(f);
}
sw_ostream(sw_ostream&& other) noexcept {
*this = std::move(other);
}
sw_ostream& operator=(sw_ostream&& other) {
if (did_construct_fstream()) {
if (other.did_construct_fstream()) {
static_cast<std::fstream&>(*os) = std::move(static_cast<std::fstream&>(*(other.os)));
} else {
destruct();
os = other.os;
}
} else {
if (other.did_construct_fstream()) {
os = new (f) std::fstream(std::move(static_cast<std::fstream&>(*other.os)));
} else {
os = other.os;
}
}
return *this;
}
sw_ostream& operator=(std::ostream& other) {
if (did_construct_fstream()) {
destruct();
}
os = &other;
return *this;
}
sw_ostream& operator=(std::fstream&& other) {
if (did_construct_fstream()) {
static_cast<std::fstream&>(*os) = std::move(other);
} else {
os = new (f) std::fstream(std::move(other));
}
return *this;
}
std::ostream& operator*() const noexcept {
return *os;
}
std::ostream* operator->() const noexcept {
return os;
}
operator std::ostream&() const noexcept {
return *os;
}
std::fstream* get_fstream() const noexcept {
if (did_construct_fstream()) return &static_cast<std::fstream&>(*os);
return dynamic_cast<std::fstream*>(os);
}
// `s << (...)` is a shorthand for `*s << (...)` (Where `s` is a `sw_ostream`)
template<class T>
const sw_ostream& operator<<(T&& o) const {
*os << std::forward<T>(o);
return *this;
}
template<class T>
sw_ostream& operator<<(T&& o) {
*os << std::forward<T>(o);
return *this;
}
~sw_ostream() {
if (did_construct_fstream()) {
destruct();
}
}
};
int main() {
sw_ostream s;
if (opening_file) {
s = std::fstream("filename");
} else {
s = std::cout;
}
if (std::fstream* fp = s.get_fstream()) {
assert(fp->is_open());
}
s << "Hello, world!\n";
s->flush();
}
I also came up with another solution that uses std::unique_ptr so that you can use any derived class of std::ostream, but that unnecessarily uses dynamic memory if you only want an existing std::ostream (Like std::cout) or a std::fstream. See here.
Related
Assume we are working with Clang strictly. No other compiler is being used. Also note that Clang supports CXX ABI.
We are using C++14.
Normally, we would get demangled class name like so:
#include <cxxabi.h>
class GoodClass {
public:
virtual const char *foo() const noexcept;
}
const char *
GoodClass::foo() const noexcept
{
// Naive implementation, not gonna' check any errors and stuff.
int32_t status = 0;
return abi::__cxa_demangle(typeid(*this).name(), 0, 0, &status);
}
This method will help us when we need class names of public subclasses of this class:
class SomeSubclassOfGoodClass : public GoodClass { }
SomeSubclassOfGoodClass object;
std::cout << object.foo(); // prints "SomeSubclassOfGoodClass"
However, in static methods, we could not use this since there is no instance. Therefore, it is impossible to serve an object to the typeid directive.
The examplary method serves well (with polymorphism), however it needs an instance to operate. This would involve problems about OO (such as constructors).
What would you do in a situation like this?
Thank you for your attention.
The use of demangle needs a little work. At the moment you have a memory leak.
Here's one way to solve that:
#include <cxxabi.h>
#include <memory>
#include <iostream>
#include <string>
#include <typeinfo>
#include <typeindex>
#include <cassert>
#include <stdexcept>
struct demangled_string
{
using ptr_type = std::unique_ptr<char, void(*)(void*)>;
demangled_string(ptr_type&& ptr) noexcept;
const char* c_str() const;
operator std::string() const;
std::ostream& write(std::ostream& os) const;
private:
ptr_type _ptr;
};
inline std::ostream& operator<<(std::ostream& os, const demangled_string& str)
{
return str.write(os);
}
inline std::string operator+ (std::string l, const demangled_string& r) {
return l + r.c_str();
}
inline std::string operator+(const demangled_string& l, const std::string& r)
{
return std::string(l) + r;
}
demangled_string demangle(const char* name);
demangled_string demangle(const std::type_info& type);
demangled_string demangle(std::type_index type);
template<class T>
demangled_string demangle(T* p) {
return demangle(typeid(*p));
}
template<class T>
demangled_string demangle()
{
return demangle(typeid(T));
}
// implementation
demangled_string::demangled_string(ptr_type&& ptr) noexcept
: _ptr(std::move(ptr))
{}
std::ostream& demangled_string::write(std::ostream& os) const
{
if (_ptr) {
return os << _ptr.get();
}
else {
return os << "{nullptr}";
}
}
const char* demangled_string::c_str() const
{
if (!_ptr)
{
throw std::logic_error("demangled_string - zombie object");
}
else {
return _ptr.get();
}
}
demangled_string::operator std::string() const {
return std::string(c_str());
}
demangled_string demangle(const char* name)
{
using namespace std::string_literals;
int status = -4;
demangled_string::ptr_type ptr {
abi::__cxa_demangle(name, nullptr, nullptr, &status),
std::free
};
if (status == 0) return { std::move(ptr) };
switch(status)
{
case -1: throw std::bad_alloc();
case -2: {
std::string msg = "invalid mangled name~";
msg += name;
auto p = (char*)std::malloc(msg.length() + 1);
strcpy(p, msg.c_str());
return demangled_string::ptr_type { p, std::free };
}
case -3:
assert(!"invalid argument sent to __cxa_demangle");
throw std::logic_error("invalid argument sent to __cxa_demangle");
default:
assert(!"PANIC! unexpected return value");
throw std::logic_error("PANIC! unexpected return value");
}
}
demangled_string demangle(const std::type_info& type)
{
return demangle(type.name());
}
demangled_string demangle(std::type_index type)
{
return demangle(type.name());
}
std::string method(const demangled_string& cls, const char* method)
{
return std::string(cls) + "::" + method;
}
// test
class test_class
{
using this_class = test_class;
static auto classname() { return demangle<this_class>(); }
public:
static void test1() {
std::cout << method(demangle<this_class>(), __func__) << std::endl;
std::cout << method(classname(), __func__) << std::endl;
}
void test2() {
std::cout << method(demangle(this), __func__) << std::endl;
std::cout << method(classname(), __func__) << std::endl;
}
};
int main()
{
test_class t;
t.test1();
t.test2();
}
expected output:
test_class::test1
test_class::test1
test_class::test2
test_class::test2
The typeid operator may also be applied to a type, not just an expression: typeid(GoodClass) ought to work when you cannot access this.
Edit: without an instance you need to turn to static polymorphism. You could have a mix in base class Identifiable<X> which has a static method with the code you suggested above, but using typeid(X) instead. Your classes need to extend this class passing themselves as the template parameter (the curiously recursive template pattern), but it is not possible to ensure that a class does so:
class C : public Identifiable<C> {}; // method returns C
class D : public Identifiable<C> {}; // also returns C
Here is the problem I was thinking about lately. Let's say our interface is a member function that returns object which is expensive to copy and cheap to move (std::string, std::vector, et cetera). Some implementations may compute the result and return a temporary object while others may simply return a member object.
Sample code to illustrate:
// assume the interface is: Vec foo() const
// Vec is cheap to move but expensive to copy
struct RetMember {
Vec foo() const { return m_data; }
Vec m_data;
// some other code
}
struct RetLocal {
Vec foo() const {
Vec local = /*some computation*/;
return local;
}
};
There are also various "clients". Some only read the data, some require an ownership.
void only_reads(const Vec&) { /* some code */ }
void requires_ownership(Vec) { /* some code */ }
Code above composes well, but is not as efficient as it could be. Here are all combinations:
RetMember retmem;
RetLocal retloc;
only_reads(retmem.foo()); // unnecessary copy, bad
only_reads(retloc.foo()); // no copy, good
requires_ownership(retmem.foo()); // copy, good
requires_ownership(retloc.foo()); // no copy, good
What is a good way to fix this situation?
I came up with two ways, but I'm sure there is a better solution.
In my first attempt I wrote a DelayedCopy wrapper that holds either a value of T or a pointer to const T. It is very ugly, requires extra effort, introduces redundant moves, gets in the way of copy elision and probably has many other problems.
My second thought was a continuation-passing style, which works quite well but turns member functions into member function templates. I know, there is std::function, but it has its overhead so performance-wise it may be unacceptable.
Sample code:
#include <boost/variant/variant.hpp>
#include <cstdio>
#include <iostream>
#include <type_traits>
struct Noisy {
Noisy() = default;
Noisy(const Noisy &) { std::puts("Noisy: copy ctor"); }
Noisy(Noisy &&) { std::puts("Noisy: move ctor"); }
Noisy &operator=(const Noisy &) {
std::puts("Noisy: copy assign");
return *this;
}
Noisy &operator=(Noisy &&) {
std::puts("Noisy: move assign");
return *this;
}
};
template <typename T> struct Borrowed {
explicit Borrowed(const T *ptr) : data_(ptr) {}
const T *get() const { return data_; }
private:
const T *data_;
};
template <typename T> struct DelayedCopy {
private:
using Ptr = Borrowed<T>;
boost::variant<Ptr, T> data_;
static_assert(std::is_move_constructible<T>::value, "");
static_assert(std::is_copy_constructible<T>::value, "");
public:
DelayedCopy() = delete;
DelayedCopy(const DelayedCopy &) = delete;
DelayedCopy &operator=(const DelayedCopy &) = delete;
DelayedCopy(DelayedCopy &&) = default;
DelayedCopy &operator=(DelayedCopy &&) = default;
DelayedCopy(T &&value) : data_(std::move(value)) {}
DelayedCopy(const T &cref) : data_(Borrowed<T>(&cref)) {}
const T &ref() const { return boost::apply_visitor(RefVisitor(), data_); }
friend T take_ownership(DelayedCopy &&cow) {
return boost::apply_visitor(TakeOwnershipVisitor(), cow.data_);
}
private:
struct RefVisitor : public boost::static_visitor<const T &> {
const T &operator()(Borrowed<T> ptr) const { return *ptr.get(); }
const T &operator()(const T &ref) const { return ref; }
};
struct TakeOwnershipVisitor : public boost::static_visitor<T> {
T operator()(Borrowed<T> ptr) const { return T(*ptr.get()); }
T operator()(T &ref) const { return T(std::move(ref)); }
};
};
struct Bar {
Noisy data_;
auto fl() -> DelayedCopy<Noisy> { return Noisy(); }
auto fm() -> DelayedCopy<Noisy> { return data_; }
template <typename Fn> void cpsl(Fn fn) { fn(Noisy()); }
template <typename Fn> void cpsm(Fn fn) { fn(data_); }
};
static void client_observes(const Noisy &) { std::puts(__func__); }
static void client_requires_ownership(Noisy) { std::puts(__func__); }
int main() {
Bar a;
std::puts("DelayedCopy:");
auto afl = a.fl();
auto afm = a.fm();
client_observes(afl.ref());
client_observes(afm.ref());
client_requires_ownership(take_ownership(a.fl()));
client_requires_ownership(take_ownership(a.fm()));
std::puts("\nCPS:");
a.cpsl(client_observes);
a.cpsm(client_observes);
a.cpsl(client_requires_ownership);
a.cpsm(client_requires_ownership);
}
Output:
DelayedCopy:
Noisy: move ctor
client_observes
client_observes
Noisy: move ctor
Noisy: move ctor
client_requires_ownership
Noisy: copy ctor
client_requires_ownership
CPS:
client_observes
client_observes
client_requires_ownership
Noisy: copy ctor
client_requires_ownership
Are there better techniques to pass values that avoid extra copies yet are still general (allow returning both temporaries and data members)?
On a side note: the code was compiled with g++ 5.2 and clang 3.7 in C++11. In C++14 and C++1z DelayedCopy doesn't compile and I'm not sure whether it's my fault or not.
There are probably thousands of 'correct' ways. I would favour one in which:
the the method that delivers the reference or moved object is explicitly stated so no-one is in any doubt.
as little code to maintain as possible.
all code combination compile and do sensible things.
something like this (contrived) example:
#include <iostream>
#include <string>
#include <boost/optional.hpp>
// an object that produces (for example) strings
struct universal_producer
{
void produce(std::string s)
{
_current = std::move(s);
// perhaps signal clients that there is something to take here?
}
// allows a consumer to see the string but does not relinquish ownership
const std::string& peek() const {
// will throw an exception if there is nothing to take
return _current.value();
}
// removes the string from the producer and hands it to the consumer
std::string take() // not const
{
std::string result = std::move(_current.value());
_current = boost::none;
return result;
}
boost::optional<std::string> _current;
};
using namespace std;
// prints a string by reference
void say_reference(const std::string& s)
{
cout << s << endl;
}
// prints a string after taking ownership or a copy depending on the call context
void say_copy(std::string s)
{
cout << s << endl;
}
auto main() -> int
{
universal_producer producer;
producer.produce("Hello, World!");
// print by reference
say_reference(producer.peek());
// print a copy but don't take ownership
say_copy(producer.peek());
// take ownership and print
say_copy(producer.take());
// producer now has no string. next peek or take will cause an exception
try {
say_reference(producer.peek());
}
catch(const std::exception& e)
{
cout << "exception: " << e.what() << endl;
}
return 0;
}
expected output:
Hello, World!
Hello, World!
Hello, World!
exception: Attempted to access the value of an uninitialized optional object.
I'm making a simple logging class with a pointer to either a std::ofstream or std::cerr.
Is there any simple way to use a smart pointer for auto clean-up regardless of which stream is used?
The code must compile on clang++, g++, and VS2013.
Code
#include <iostream>
#include <fstream>
#include <string>
class Logger {
private:
std::ostream * output_stream{ nullptr };
bool using_file{ false };
public:
Logger()
{
output_stream = &std::cerr;
using_file = false;
}
Logger(std::string file)
{
output_stream = new std::ofstream(file);
using_file = true;
}
~Logger()
{
if (using_file)
{
delete output_stream;
}
}
template<typename T>
void log(T info)
{
*output_stream << info << std::endl;
}
};
class tmp {
int i{ 4 };
friend std::ostream & operator<<(std::ostream &os, const tmp& p);
};
std::ostream &operator<<(std::ostream &os, const tmp& p)
{
return os << p.i;
}
int main()
{
tmp t;
Logger logger;
logger.log(t);
system("pause");
return 0;
}
Attempts
std::unique_ptr
I can use std::unique_ptr for the file like so:
std::unique_ptr<std::ostream> p;
p = std::make_unique<std::ofstream>("file.txt");
*p << "hi there" << std::endl;
Trying this with std::cout warns me about a deleted function (assuming that's the constructor.
std::unique_ptr<std::ostream> p2;
p2 = std::make_unique<std::ostream>(std::cout);
*p2 << "hey" << std::endl;
std::shared_ptr
Because std::unique_ptr is only for owning things, and std::cout shouldn't be owned, I thought I'd try std::shared_ptr
std::shared_ptr<std::ostream> p;
p = std::make_shared<std::ostream>(std::cout);
*p << "hola" << std::endl;
It gives me the same deleted constructor error. p = &std::cout complains about a type mismatch, so it's also not working.
You can use a shared_ptr with a deleter that does not delete anything in the case of cerr and just a normally constructed shared_ptr in the case of ofstream
class Logger {
private:
std::shared_ptr<std::ostream> output_stream{ nullptr };
public:
Logger() :
output_stream(&std::cerr, [](std::ostream*){})
{ }
Logger(std::string file) :
output_stream(std::make_shared<std::ofstream>(file))
{ }
// default destructor is OK
template<typename T>
void log(T info)
{
*output_stream << info << std::endl;
}
};
I would just have two pointers, one smart and one raw.
The raw pointer is always used to refer to the stream. The smart pointer is just used for clean-up if needed.
class Logger {
private:
std::unique_ptr<std::ofstream> file_stream;
std:ostream *stream;
public:
Logger() : stream(&std::cerr) {}
Logger(const std::string& file)
: file_stream(std::make_unique<std::ofstream>(file)), stream(file_stream.get()){}
template<typename T>
void log(T info) {
*stream << info << std::endl;
}
};
I tend to try to avoid cases where I want an object to "own" such things. In the times I did not have much choice, I ended up settling with a "shouldDelete" flag or a callback.
class Logger {
public:
Logger(std::ofstream *outputStream, bool deleteOutputStream)
: outputStream(outputStream), deleteOutputStream(deleteOutputStream)
{ }
~Logger()
{
if (deleteOutputStream) delete outputStream;
}
};
Logger logger(&std::cout, false);
class Logger {
public:
typedef std::function<void(std::ostream*)> Deleter;
typedef std::unique_ptr<std::ostream, Deleter> OStreamPointer;
Logger(OStreamPointer &&outputStream)
: outputStream(std::move(outputStream))
{ }
~Logger() { }
private:
OStreamPointer outputStream;
};
Logger logger(Logger::OStreamPointer(
&std::cout,
[](std::ostream*) {})); //no-op callback
You could do this by releasing the smart pointer in the destructor (and elsewhere) in the cases where it shouldn't be deleted, but that's not worth the hassle IMO.
Instead, I'd recommend simply using two pointers: one for streams that need to be managed and one for those that don't:
class Logger {
private:
std::ostream * unmanaged_stream{ nullptr };
std::unique_ptr<std::ostream> managed_stream{ nullptr };
bool using_file{ false };
std::ostream& output_stream()
{
return using_file ? *managed_stream : *unmanaged_stream;
}
public:
Logger()
: unmanaged_stream{&std::cerr},
using_file{false}
{
}
Logger(const std::string& file)
: managed_stream{std::make_unique<std::ofstream>(file)},
using_file{true}
{
}
template<typename T>
void log(T info)
{
output_stream() << info << std::endl;
}
};
If saving space is a priority you could put them in a union, but then you'd have to explicitly call the destructor and placement new to define the active member, which again is more hassle and probably not worth it.
According to cppreference calling std::unique_ptr::operator*() is equivalent to calling *(std::unique_ptr::get()).
However I'm getting different results for both calls. Here is my code:
#include <iostream>
#include <string>
#include <memory>
#include <fcntl.h>
#include <unistd.h>
struct file_descriptor
{
private:
struct closer;
public:
typedef int handle_type;
typedef closer closer_type;
constexpr static handle_type kInvalidHandle = -1;
public:
file_descriptor(int handle = kInvalidHandle) : handle_{ handle } { }
file_descriptor(std::nullptr_t) : file_descriptor{ } { }
operator int&() { return handle_; }
operator int() const { return handle_; }
int& operator*() { return static_cast<int&>(*this); }
int operator*() const { return static_cast<int>(*this); }
bool operator==(const file_descriptor& other) const
{ return (handle_ == other.handle_); }
bool operator!=(const file_descriptor& other) const
{ return !(*this == other); }
private:
struct closer
{
typedef file_descriptor pointer;
void operator()(pointer handle) const
{ ::close(*handle); }
};
int handle_;
};
using unique_file_ptr = std::unique_ptr<typename file_descriptor::handle_type,
typename file_descriptor::closer_type>;
unique_file_ptr managed_open(const std::string& path)
{
return { ::open(path.c_str(), O_RDWR), { } };
}
int main(int, char**)
{
auto handle = managed_open("/dev/random");
std::cout << "*handle : " << *handle << std::endl;
std::cout << "*handle.get(): " << *handle.get() << std::endl;
}
My output (live output here):
*handle : 4198400
*handle.get(): 3
Please note that *handle.get() returns the correct value, while *handle doesn't.
Why am I getting different results?
Here's what happens. unique_ptr<T, D>::get() returns D::pointer - in your case, a temporary file_descriptor constructed from the int handle. Then its operator* calls its operator int& which returns a reference to handle_ stored inside that temporary.
When you call *handle.get() directly, the int& reference it produces is used before the temporary dies.
But when you call *handle instead, you call handle.operator*() which in turn calls handle.get(). The implementation of operator*, with everything expanded, becomes something like this:
int& operator*() {
file_descriptor temp(internal_pointer_);
int& result = temp.handle_;
return result;
}
The function returns a reference to the member of a temporary. That temporary dies at the closing brace, and the reference becomes dangling, whereupon the program exhibits undefined behavior triggered by accessing an object after its lifetime has ended.
I suggest you re-think the solution. Here's an arguably cleaner way to approach it:
#include <iostream>
#include <string>
#include <memory>
#include <stdexcept>
#include <utility>
#include <fcntl.h>
#include <unistd.h>
struct managed_file {
managed_file() noexcept {};
managed_file(const std::string& path)
: _handle { ::open(path.c_str(), O_RDWR) }
{
if (_handle == -1) {
throw std::runtime_error { std::string { "failed to open file " } + path };
}
}
managed_file(const managed_file&) = delete;
managed_file(managed_file&& other) noexcept
: _handle { other._handle }
{
other._handle = -1;
}
managed_file& operator=(const managed_file&) = delete;
managed_file& operator=(managed_file&& other) noexcept {
managed_file tmp { std::move(other) };
using std::swap;
swap(_handle, other._handle);
return *this;
}
virtual ~managed_file() noexcept
{
close();
}
void close() noexcept {
if (_handle != -1) {
::close(_handle);
_handle = -1;
}
}
const int& operator*() const noexcept {
return _handle;
}
private:
int _handle = -1;
};
managed_file managed_open(const std::string& path)
{
return managed_file { path };
}
using namespace std;
int main(int, char**)
{
cout << "opening" << endl;
auto handle = managed_open("/dev/random");
cout << "checking" << endl;
std::cout << "*handle : " << *handle << std::endl;
}
I'm trying to fix a double free or corruption in this class:
struct Holder
{
template <typename T>
Holder(const T& v)
{
_v = new T{};
memcpy(_v, &v, sizeof(T));
_deleter = [this]{
if (_v != nullptr)
{
delete reinterpret_cast<T*>(_v);
_v = nullptr;
}
};
}
template <typename T>
T get()
{
T t;
memcpy(&t, _v, sizeof(T));
return t;
}
~Holder()
{
std::cout << "~Holder() " << std::endl;
_deleter();
}
private:
void* _v;
std::function<void()> _deleter;
};
The goal of this class is to Hold a value of a particular type, like boost::any. So I'm trying to understand the mechanism to safely deallocate all memory.
Probably this line of code:
delete reinterpret_cast<T*>(_v);
doesn't do what I expect ...
**** After Suggestions ****
I've rewrite the code using comment suggestions and adding a move constructor
struct Holder
{
template <typename T>
Holder(const T& v)
{
std::cerr << "create " << N << std::endl;
_v = new T(v);
_deleter = [this]{
if (_v != nullptr)
{
std::cerr << "deleter " << N << std::endl;
delete reinterpret_cast<T*>(_v);
_v = nullptr;
}
};
}
Holder(Holder&& rs)
{
_v = rs._v;
_deleter = std::move(rs._deleter);
rs._deleter = []{}; //usefull to avoid a bad function call
}
template <typename T>
T get() const
{
return *reinterpret_cast<T*>(_v);
}
~Holder()
{
//std::cout << "~Holder() " << N << std::endl;
_deleter();
}
private:
void* _v;
std::function<void()> _deleter;
};
Now seems work but I have to manage others corner case :)
Probably the best solution is to use boost::any:
struct Holder
{
template <typename T>
Holder(const T& v)
{
_v = v;
}
template <typename T>
T get()
{
return boost::any_cast<T>(_v);
}
private:
boost::any _v;
};
But I'am trying to understand how it coudl works without it.
This is my last version:
struct Holder
{
template <typename T>
Holder(const T& v)
{
std::cerr << "create " << N << std::endl;
_v = new T(v);
_deleter = [](void* ptr){
if (ptr != nullptr)
{
std::cerr << "deleter " << std::endl;
delete reinterpret_cast<T*>(ptr);
}
};
_builder = [](void* &dest, void* src){
dest = new T(*reinterpret_cast<T*>(src));
};
}
Holder(const Holder& rs)
{
std::cerr << "copy constr" << std::endl;
if (this != &rs)
{
rs._builder(_v, rs._v);
_deleter = rs._deleter;
_builder = rs._builder;
}
}
Holder(Holder&& rs)
{
std::cerr << "move constr" << std::endl;
if (this != &rs)
{
_v = rs._v;
_deleter = std::move(rs._deleter);
_builder = std::move(rs._builder);
rs._deleter = [](void*){};
}
}
Holder& operator=(const Holder& rs)
{
std::cerr << "copy operator" << std::endl;
if (this != &rs)
{
rs._builder(_v, rs._v);
_deleter = rs._deleter;
_builder = rs._builder;
}
return *this;
}
Holder& operator=(Holder&& rs)
{
std::cerr << "move operator" << std::endl;
if (this != &rs)
{
_v = rs._v;
_deleter = std::move(rs._deleter);
_builder = std::move(rs._builder);
rs._deleter = [](void*){};
}
return *this;
}
template <typename T>
T get() const
{
return *reinterpret_cast<T*>(_v);
}
~Holder()
{
//std::cout << "~Holder() " << N << std::endl;
_deleter(_v);
}
private:
void* _v;
std::function<void(void* ptr)> _deleter;
std::function<void(void* &, void* src)> _builder;
};
Don't reimplement the horse.
using pvoid_holder = std::unique_ptr<void, std::function<void(void*)>>
template<class T>
pvoid_holder pvoid_it( T* t ) {
return { t, [](void* v){ if (v) delete static_cast<T*>(v); } };
}
Now store a pvoid_holder in your Holder class. It will handle memory lifetime for you.
You could use a naked pvoid_holder, but it might have a richer interface than you want (for example, it will allow the stored pointer to be changed without changing the deleter).
You can also replace std::function with void(*)(void*) for a marginal performance gain.
Here is a random idea. I still don't like it though. The whole idea behind this design is bad.
template <typename T>
struct Holder
{
public:
Holder(T const& v)
{
new (&m_v) T(v);
}
T const& get() const
{
return reinterpret_cast<T const&>(m_v);
}
T& get()
{
return reinterpret_cast<T&>(m_v);
}
~Holder()
{
std::cout << "~Holder() " << std::endl;
get().~T();
}
private:
char m_v[sizeof(T)];
};
This class doesn't do the same as yours anymore, ie it can't store arbitrary types in a std::vector<Holder> but only the same type (std::vector<Holder<Foo>>). A comment was too small to contain this code though and I wanted to show a better looking syntax for what you're playing with ;).
That being said, the only way you can do what you're trying to do is when you add a second layer for the reference counting. Ie, you replace your void* _v with something that resembles shared_ptr but which doesn't call delete when the count reaches zero but calls deleter (which therefore should be stored inside this new class). In fact your class looks mostly like this new class, except that you should make it non-copyable and provide reference counting (ie through boost::intrusive_ptr). Then Holder can be a wrapper around that that is copyable.
Probably this line of code: delete reinterpret_cast<T*>(_v); doesn't do what I expect ...
Not exactly. Your types are likely using a default copy ctor; this copies your data pointer _v, and your deleter. So when both objects destruct, both deleters trigger, causing the data to be deleted twice. (Side note--you shouldn't name variables starting with _; such identifiers are reserved for implementations).
Here's what it takes to do type erasure properly, assuming I've no bugs in it. A better way would be to stick to using boost::any.
#include <utility>
struct EmptyType {}; // Thrown if unexpectedly empty
struct InvalidType {}; // Thrown if Holder(T) but get<U>.
struct Holder
{
Holder()
: data_()
, deleter_(e_deleter)
, copier_(e_copier)
, typetag_()
{
}
template<typename T>
Holder(const T& t)
: data_(erase_cast(new T))
, deleter_(deleter<T>)
// Need to explicitly carry T's copy behavior
// because Holder's default copy ctor isn't going to
, copier_(copier<T>)
// You need some way to protect against getting
// an Orange out of a Holder that holds an Apple.
, typetag_(id<T>())
{
}
Holder(const Holder& rhs)
: data_(rhs.copy())
, deleter_(rhs.deleter_)
, copier_(rhs.copier_)
, typetag_(rhs.typetag_)
{
}
template<typename T>
T get()
{
if (!data_) throw EmptyType();
T rv(fetch<T>());
return rv;
}
Holder(Holder&& rhs)
: data_()
, copier_(rhs.copier_)
, deleter_(rhs.deleter_)
, typetag_(rhs.typetag_)
{
std::swap(data_, rhs.data_);
}
~Holder()
{
destroy();
}
private:
// Reinterpret_cast wrappers labeled semantically
template<typename T>
static void* erase_cast(T* t) { return reinterpret_cast<void*>(t); }
template<typename T>
static T* unerase_cast(void* t) { return reinterpret_cast<T*>(t); }
// Return a data copy
void* copy() const { return copier_(data_); }
// Return const reference to data
template<typename T>
const T& fetch() {
if (typetag_!=id<T>()) throw InvalidType();
return *unerase_cast<T>(data_);
}
// Destroy data
void destroy() { deleter_(data_); data_=0; }
// ==== Type erased copy semantics ===
void*(*copier_)(void*);
template<typename T>
static void* copier(void* v) {
return erase_cast<T>(new T(*unerase_cast<T>(v)));
}
static void* e_copier(void*) { return 0; }
// ==== Type erased delete semantics ===
void(*deleter_)(void*);
template<typename T>
static void deleter(void* v) {
delete unerase_cast<T>(v);
}
static void e_deleter(void*) {}
// ==== Type protection using tagging (could also use typeid)
static int makenewid() { static int i=0; return i++;}
template<typename T>
static int id() { static int i=makenewid(); return i; }
// Type erased data
void* data_;
// Type erased tag
int typetag_;
};
...and here is some test/demo code:
#include <iostream>
#include <vector>
#define FAIL() std::cout << "Fail" << std::endl; return 1
int foos=0;
struct Foo { Foo(){++foos;} Foo(const Foo&){++foos;} ~Foo(){--foos;} };
int bars=0;
struct Bar { Bar(){++bars;} Bar(const Bar&){++bars;} ~Bar(){--bars;} };
int main() {
{
std::vector<Holder> v;
Foo fx,fy,fz; Bar ba,bb;
v.push_back(fx); v.push_back(fy); v.push_back(fz);
v.push_back(ba); v.push_back(ba); v.push_back(bb);
v.push_back(Holder());
try {
Foo y = v[2].get<Foo>();
}
catch (EmptyType&) { FAIL(); }
catch (InvalidType&) { FAIL(); }
try {
Foo y = v[4].get<Foo>();
FAIL();
}
catch (EmptyType&) { FAIL(); }
catch (InvalidType&) { }
try {
Foo y = v[6].get<Foo>();
FAIL();
}
catch (EmptyType&) { }
catch (InvalidType&) { FAIL(); }
}
if (foos||bars) { FAIL(); }
std::cout << "Pass" << std::endl;
}
Test results:
$ ./a.exe
Pass