How does "constness" propagate through calls to overloaded operator->()? - c++

I've implemented a thread-safe wrapper for arbitrary objects that uses the drill down behavior of operator->(), my problem is that I don't quite understand how constness of the wrapped object is propagated through the calls to the operator->() down to the wrapped object pointer:
const T *operator->() const
{
mtx_.lock();
return const_cast<const T *>(t_);
}
is never called in the Protector class, even though ThreadSafe<const std::string> won't call a non-const method on the string it wraps, declaring it as const ThreadSafe<const std::string> changes nothing.
Could you explain why this is the case - the non-const version of the operator is called, but the end result is as if the const version of the operator was called.
#include <algorithm>
#include <boost/type_index.hpp>
#include <cassert>
#include <future>
#include <iostream>
#include <vector>
template <typename T>
class ThreadSafe
{
public:
template <class... Args>
explicit ThreadSafe(Args &&...args)
: obj_{std::forward<Args>(args)...}
{
}
auto operator->()
{
return Protector(&obj_, mtx_);
}
auto operator->() const
{
return Protector(std::add_const_t<T *>(&obj_), mtx_);
}
// method strictly for debugging
T copy() const
{
std::lock_guard<std::mutex> lck(mtx_);
return obj_;
}
private:
struct Protector
{
explicit Protector(T *t, std::mutex &mtx)
: mtx_(mtx)
, t_(t)
{
}
T *operator->()
{
mtx_.lock();
return t_;
}
const T *operator->() const
{
mtx_.lock();
return const_cast<const T *>(t_);
}
~Protector()
{
mtx_.unlock();
}
std::mutex &mtx_;
T *t_;
};
mutable std::mutex mtx_;
T obj_;
};
int main()
{
// ThreadSafe<std::string> ts_i{"123456789"};
// ts_i->append("0");
// std::cout << ts_i->c_str() << std::endl;
const ThreadSafe<const std::string> cts_i{"asd123"};
// cts_i->append("10"); // non const methods are inaccessible with a ThreadSafe<const T> wrapper, but const methods are.
std::cout << cts_i->substr(3) << std::endl;
return 0;
}

Both of these return the same type (non-const Protector), so naturally chained operator->() is treated the same in both cases:
auto operator->()
{
return Protector(&obj_, mtx_);
}
auto operator->() const
{
return Protector(std::add_const_t<T *>(&obj_), mtx_);
}
The second one wouldn't even compile if T is not const-qualified already.

Related

Default reference parameters and lifetimes in coroutines

I'm confused about the lifetime of parameters passed to C++ coroutines.
Answering to a previous question, smart people stated that
The lifetime of a parameter is [...] part of the caller's scope
Now, to follow up, what happens when passing default arguments like
generator my_coroutine(string&& s = string()) {...}
So, if my_coroutine was a normal function, s would be valid throughout its scope. However, this seems to no longer hold if my_coroutine is a coroutine.
In particular the results of the following coroutine-test surprised me:
#include <iostream>
#include <coroutine>
struct Test {
int i = 3;
Test() { std::cout << "test constructed\n";}
Test(const Test&) = delete;
Test(Test&&) = delete;
~Test() { std::cout << "test destructed\n"; }
friend std::ostream& operator<<(std::ostream& os, const Test& t) { return os << t.i; }
};
template<class T>
generator<int> coro_test(T&& t = T()) {
int i = 0;
while(i++ < 3) co_yield i;
if(i == t.i) co_yield 100;
}
int main () {
auto gen = coro_test<Test>();
while(gen.is_valid()) {
std::cout << *gen << "\n";
++gen;
}
return 0;
}
results:
test constructed
test destructed
1
2
3
PS: for completeness, here's my generator:
template<class T>
struct generator {
struct promise_type;
using coro_handle = std::coroutine_handle<promise_type>;
struct promise_type {
T current_value;
auto get_return_object() { return generator{coro_handle::from_promise(*this)}; }
auto initial_suspend() const noexcept { return std::suspend_never{}; }
auto final_suspend() const noexcept { return std::suspend_always{}; }
void unhandled_exception() const { std::terminate(); }
template<class Q>
auto yield_value(Q&& value) {
current_value = std::forward<Q>(value);
return std::suspend_always{};
}
};
private:
coro_handle coro;
generator(coro_handle h): coro(h) {}
public:
bool is_valid() const { return !coro.done(); }
generator& operator++() { if(is_valid()) coro.resume(); return *this; }
T& operator*() { return coro.promise().current_value; }
const T& operator*() const { return coro.promise().current_value; }
generator(const generator&) = delete;
generator& operator=(const generator&) = delete;
~generator() { if(coro) coro.destroy(); }
};
As pointed out in said "previous question", the first thing that happens in a coroutine is that parameters are "copied" into storage owned by the coroutine. However, the "copy" is ultimately initialized based on the type declared in the signature. That is, if a parameter is a reference, then the "copy" of that parameter is also a reference.
So a coroutine function that takes reference parameters is much like any kind of asynchronous function that takes reference parameters: the caller must ensure that the referenced object continues to exist throughout the time that the object will be used. A default parameter which initializes a reference is a circumstance that the caller cannot control the lifetime of (other than providing an explicit parameter).
You created an API that is inherently broken. Don't do that. Indeed, it's best to avoid passing references to async functions of any kind, but if you do, never give them default parameters.

C++ override member access operator for template class and return by reference

Is it possible to override the -> operator in template class and return something by reference?
I saw this post: Overloading member access operators ->, .*
And there is an example of overriding -> and return by reference, but I can't get this to work with templates. Here's a small example of what I'm trying to achieve:
#include <iostream>
using namespace std;
class A
{
public:
void do_something()
{
cout << "Hey there";
}
};
template<class T>
class Ref
{
public:
Ref(T* ptr)
{
objPtr = ptr;
}
// this is another alternative, but I don't want to write Get() every time I want to access the object
T& get() { return *objPtr; }
template <class T>
Ref<T>& operator->() const { return *objPtr; }
// doesn't work either
//T& operator->() const { return *objPtr; }
// this works as expected, but I really MUST return by reference
//T* operator->() const { return objPtr; }
private:
T* objPtr;
};
int main()
{
A myObj;
Ref<A> ref(&myObj);
// error C2675: unary '->': 'Ref<A>' does not define this operator or a conversion to a type acceptable to the predefined operator
ref->do_something();
return 0;
}
How can this be done?
If you return a reference, you can't use it in ref->do_something(); which requires a pointer. You'd have to use this cumbersome method:
ref.operator->().do_something();
Instead return a pointer - and make it a T* (or const T*), not a Ref<T>*.
Example:
#include <iostream>
class A {
public:
void do_something() {
std::cout << "Hey there\n";
}
};
template<class T>
class Ref {
public:
Ref(T& ptr) : objPtr(&ptr) {} // taking a T& but storing a pointer
const T* operator->() const { return objPtr; }
T* operator->() { return objPtr; }
private:
T* objPtr;
};
int main() {
A myObj;
Ref<A> ref(myObj);
ref->do_something();
}

What are good ways to avoid copying if method's caller doesn't need ownership of the data?

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.

How to pass an operator()() of a functor allocated on the heap, to a thread?

For the functor:
class F
{
bool proceed;
public:
F():proceed(true) {}
void operator()()
{
while(proceed) {std::cout<<".";}
std::cout<<"stopped\n";
}
void stopIt() {proceed=false;}
};
I'm able to create an object and pass it to a TBB thread (it's now under the std namespace):
F foo;
std::thread t(foo);
This works fine, except that when I call foo.stopIt(), the while loop does not stop. So I assume the foo I'm passing to the thread actually creates a new object.
To prevent that, I tried:
F* foo = new F();
But std::thread t(foo); doesn't work. t(*foo) doesn't work. t(&foo) doesn't work. t(foo->()) doesn't. Tried a few other techniques too. No luck.
So how can I pass the operator()() function to the thread and be able to call stopIt() later?
UPDATE:
In response to Nathan's answer, this is what I tried:
http://coliru.stacked-crooked.com/a/36d70da08c1210f1
#include <algorithm>
#include <list>
#include <vector>
#include <iostream>
#include <numeric>
//#include <random>
#include <functional>
#include <tbb/compat/thread>
template< class T >
T* addressof(T& arg)
{
return reinterpret_cast<T*>(
&const_cast<char&>(
reinterpret_cast<const volatile char&>(arg)));
}
template <class T>
class reference_wrapper {
public:
// types
typedef T type;
// construct/copy/destroy
reference_wrapper(T& ref) : _ptr(addressof(ref)) {}
//reference_wrapper(T&&) = delete;
//reference_wrapper(const reference_wrapper&) noexcept = default;
// assignment
//reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;
// access
operator T& () const { return *_ptr; }
T& get() const { return *_ptr; }
private:
T* _ptr;
};
class F
{
bool proceed;
public:
F():proceed(true) {}
void operator()()
{
while(proceed) {std::cout<<".";}
std::cout<<"stopped\n";
}
void stopIt() {proceed=false;}
};
class ref {
public:
typedef F type;
ref(F& ref) : _ptr(addressof(ref)) {}
operator F& () const { return *_ptr; }
F& get() const { return *_ptr; }
private:
F* _ptr;
};
int main()
{
//using namespace std::literals;
F foo;
std::thread t(ref(foo));
std::cout<<"running";
//std::this_thread::sleep_for(1ms);
foo.stopIt();
//t.join();
}
thread takes it parameters by value. If you need the thread to get the same object that you have in the calling function then you need to wrap it with std::ref
In this case to run the code and get some output we can run:
int main()
{
using namespace std::literals;
F foo;
std::thread t(std::ref(foo));
std::this_thread::sleep_for(1ms);
foo.stopIt();
t.join();
}
Live Example

boost unable to move a scoped_lock with gcc

The following compiles under VS2010 (Express) but not gcc (4.6.2 here).
Lockable.h:
#include <boost/thread/mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
template<typename T>
class LockedProxy : boost::noncopyable
{
public:
inline LockedProxy(boost::mutex & m, T * obj)
:lock(m),
t(obj)
{}
inline LockedProxy(const LockedProxy && other)
:lock(std::move(other.lock)),
t(std::move(other.t))
{}
inline T * operator->() { return t; }
inline const T * operator->() const { return t; }
inline const T & operator*() const { return *t; }
inline T & operator*() { return *t; }
private:
boost::interprocess::scoped_lock<boost::mutex> lock;
T * t;
};
template<typename T>
class Lockable
{
public:
// Convenience typefed for subclasses to use
typedef T LockableObjectType;
inline Lockable(const T & t)
:lockableObject(t)
{}
inline LockedProxy<LockableObjectType> GetLockedProxy() {
return LockedProxy<LockableObjectType>(mutex, &lockableObject);
}
protected:
LockableObjectType lockableObject;
boost::mutex mutex;
};
main.cpp:
#include <iostream>
#include <string.h>
#include "Lockable.h"
void f(Lockable<std::string> & str)
{
auto proxy = str.GetLockedProxy();
*proxy = "aa";
proxy->append("bb");
std::cout << "str = " << *proxy << std::endl;
}
void g(Lockable<int> & i)
{
{ // reduce lock's lifespan
auto proxy = i.GetLockedProxy();
*proxy = 321;
}
// relock, lock lives for the statement
std::cout << "i = " << *i.GetLockedProxy() << std::endl;
}
int main()
{
Lockable<std::string> str("abc");
//Can't use str here, it is not locked
f(str);
Lockable<int> i(123);
g(i);
return 0;
}
The errors:
In file included from main.cpp:3:0:
C:/Users/DrGibbs/Documents/code/boost_1_46_0/boost/interprocess/sync/scoped_lock.hpp: In constructor 'LockedProxy<T>::LockedProxy(const LockedProxy<T>&&) [with T = std::basic_string<char>, LockedProxy<T> = LockedProxy<std::basic_string<char> >]':main.cpp:7:37: instantiated from here
C:/Users/DrGibbs/Documents/code/boost_1_46_0/boost/interprocess/sync/scoped_lock.hpp:56:4: error: 'boost::interprocess::scoped_lock<Mutex>::scoped_lock(const boost::interprocess::scoped_lock<Mutex>&)[with Mutex = boost::mutex, boost::interprocess::scoped_lock<Mutex> = boost::interprocess::scoped_lock<boost::mutex>]' is privateLockable.h:14:29: error: within this context
C:/Users/DrGibbs/Documents/code/boost_1_46_0/boost/interprocess/sync/scoped_lock.hpp: In constructor 'LockedProxy<T>::LockedProxy(const LockedProxy<T>&&) [with T = int, LockedProxy<T> = LockedProxy<int>]':
main.cpp:18:36: instantiated from here
C:/Users/DrGibbs/Documents/code/boost_1_46_0/boost/interprocess/sync/scoped_lock.hpp:56:4: error: 'boost::interprocess::scoped_lock<Mutex>::scoped_lock(const boost::interprocess::scoped_lock<Mutex>&)[with Mutex = boost::mutex, boost::interprocess::scoped_lock<Mutex> = boost::interprocess::scoped_lock<boost::mutex>]' is private
Lockable.h:14:29: error: within this context
Well as far as I understand, in LockedProxy's move-constructor, the scoped_lock is not moved but copy-constructed, which really should not work. Shouldn't the std::move guanrantee its move-construction ? What am I missing ?
Your move constructor declares its parameter const:
inline LockedProxy(const LockedProxy && other)
It should be declared non-const:
inline LockedProxy(LockedProxy && other)
Your std::move(other.lock) is taking other.lock as a const reference, and so returning a const rvalue reference const boost::interprocess::scoped_lock<boost::mutex> &&, which cannot be passed to the move constructor of boost::interprocess::scoped_lock<boost::mutex>.
See also C++0x const RValue reference as function parameter, which explains that const rvalue references are almost entirely useless.