I have a generic class, where two of the generics are going to be identical 90% of the time, but on the off chance that they are separate types, I need to perform member initialization a little bit differently.
template<typename S, typename T = S>
class MyClass {
MyClass(const Options<S> & opts) {
if (std::is_same<S,T>::value) {
thing.reset(new T(opts.get_s_thing())); // 90% of time, use type S in args to initialize thing
} else {
thing.reset(new T()); // Special case, types differ, so create a new one
}
//
// Other constructor things...
//
}
std::shared_ptr<T> thing;
};
Only thing is, since type-checking is done at compile time, the thing.reset(opts.get_s_thing()); line throws an error if I create MyClass with an explicitly different type for T. But because the types differ, that line would never get called anyways. Is there some preprocessor directive I can use here? What strategy should I take?
Changing the constructor signature in MyClass is not an option, since this is part of a VERY large codebase. T is a new thing, so I have more freedom in deciding the "standard interface" for Ts.
I would separate the two constructors. And take the rest of the current constructor code apart, e.g. to an initialize function.
[c++11] Using enable_if. [Demo]
template <typename S, typename T = S>
struct MyClass {
void initialize() {
std::cout << "initialize\n";
//
// Other constructor things...
//
}
template <typename U = S>
MyClass(const Options<typename std::enable_if<std::is_same<U,T>::value, U>::type>& opts) {
std::cout << "Same same, ";
thing.reset(new S(opts.get_s_thing()));
initialize();
}
template <typename U = S>
MyClass(const Options<typename std::enable_if<not std::is_same<U,T>::value, U>::type>& opts) {
std::cout << "But different, ";
thing.reset(new T());
initialize();
}
std::shared_ptr<T> thing;
};
[c++20] Using a requires clause. [Demo]
template <typename S, typename T = S>
struct MyClass {
void initialize()
{
std::cout << "initialize\n";
//
// Other constructor things...
//
}
MyClass(const Options<S>& opts) requires std::is_same<S,T>::value {
std::cout << "Same same, ";
thing.reset(new S(opts.get_s_thing()));
initialize();
}
MyClass(const Options<S>& opts) {
std::cout << "But different, ";
thing.reset(new T());
initialize();
}
std::shared_ptr<T> thing;
};
Other options if you want to keep the current signature completely unchanged are:
[c++11] Using partial specializations of a helper class. [Demo]
template <typename U, typename V, bool ConstructorsAreEqual>
struct MyThingInitializer;
template <typename U, typename V>
struct MyThingInitializer<U, V, true> {
static void initialize(const Options<U>& opts, std::shared_ptr<V> thing) {
std::cout << "Same same\n";
thing.reset(new V(opts.get_s_thing()));
}
};
template <typename U, typename V>
struct MyThingInitializer<U, V, false> {
static void initialize(const Options<U>&, std::shared_ptr<V> thing) {
std::cout << "But different\n";
thing.reset(new V());
}
};
MyClass(const Options<S>& opts) {
MyThingInitializer<S, T, std::is_same<S, T>::value>::initialize(opts, thing);
//
// Other constructor things...
//
}
[c++20] Using if constexpr. [Demo]
MyClass(const Options<S>& opts) {
if constexpr(std::is_same<S,T>::value)
{
std::cout << "Same same\n";
thing.reset(new S(opts.get_s_thing()));
}
else
{
std::cout << "But different";
thing.reset(new T());
}
//
// Other constructor things...
//
}
In another order of things, it's not recommended to use new with smart pointers. You may want to change your lines:
thing.reset(new S(opts.get_s_thing()));
thing.reset(new T());
for:
std::make_shared<U>(opts.get_s_thing()).swap(thing);
std::make_shared<T>().swap(thing);
Related
Boost's program_options library now supports boost::optional, can the same be done with std::optional?
I attempted to modify both the documentation example and the code in the PR, but neither seems to work.
For example, the very simple case for integers (before trying template specializations):
void validate(boost::any& v, const std::vector<std::string>& values, std::optional<int>* target_type,
int) {
using namespace boost::program_options;
validators::check_first_occurrence(v);
const string& s = validators::get_single_string(values);
int n = lexical_cast<int>(s);
v = any(std::make_optional<int>(n));
}
fails with the error that the target type is not istreamable:
external/boost/boost/lexical_cast/detail/converter_lexical.hpp:243:13:
error: static_assert failed due to requirement
'has_right_shift<std::__1::basic_istream<char>, std::__1::optional<int>, boost::binary_op_detail::dont_care>::value || boost::has_right_shift<std::__1::basic_istream<wchar_t>, std::__1::optional<int>, boost::binary_op_detail::dont_care>::value'
"Target type is neither std::istream`able nor std::wistream`able"
The problem with things like validate (and operator>> as well) is often ADL¹.
You need to declare the overload in one of the associated namespaces. In this case, because int is a primitive type, the only associated namespaces come from library code:
std for optional, vector, string, allocator, char_traits (yes these all count!)
boost for any
You'd prefer not to add your code in those namespaces, because you might interfere with library functions or invite future breakage when the library implementation details change.
If you had to choose, you'd prefer to choose boost here, because
that's the library providing the feature at hand
the validate free function is explicitly designed to be an customization point
Sidenote: Keep an eye out for tag_invoke - a better way to build customization points in libraries
The Fix
After all this verbiage, the solution is very simple:
namespace boost {
void validate(boost::any& v, const std::vector<std::string>& values,
std::optional<int>*, int) {
using namespace boost::program_options;
validators::check_first_occurrence(v);
const std::string& s = validators::get_single_string(values);
int n = boost::lexical_cast<int>(s);
v = boost::any(std::make_optional<int>(n));
}
} // namespace boost
Adding two lines made it work: Live On Wandbox.
Other Notes:
The "solution" injecting operator>> in general is less pure
because
it has a potential to "infect" all other code with ADL-visible overloads that might interfere. Way more code uses operator>> than
boost's validate function
it thereby invites UB due to
ODR violations,
when another translation unit, potentially legitimely, defines
another operator>> for the same arguments.
On recent compilers you can say vm.contains instead of the slightly abusive vm.count
There's another snag with non-streamable types, where, if you define a default value, you probably also need to specify the string representation with it.
Listing
Compiling on Compiler Explorer
#include <boost/program_options.hpp>
#include <optional>
#include <iostream>
namespace po = boost::program_options;
namespace boost {
void validate(boost::any& v, const std::vector<std::string>& values,
std::optional<int>*, int) {
using namespace boost::program_options;
validators::check_first_occurrence(v);
const std::string& s = validators::get_single_string(values);
int n = boost::lexical_cast<int>(s);
v = boost::any(std::make_optional<int>(n));
}
} // namespace boost
int main(int ac, char* av[]) {
try {
using Value = std::optional<int>;
po::options_description desc("Allowed options");
desc.add_options()
("help", "produce help message")
("value", po::value<Value>()->default_value(10, "10"),
"value")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
if (vm.contains("value")) {
std::cout << "value is " << vm["value"].as<Value>().value() << "\n";
}
} catch (std::exception& e) {
std::cout << e.what() << "\n";
return 1;
}
}
BONUS
As an added exercise, let's demonstrate that if your optional value_type is not a primitive, but rather your library type, declared in a namespace MyLib, then we don't have most of the trade-offs above:
namespace MyLib {
template <typename T> struct MyValue {
MyValue(T v = {}) : value(std::move(v)) {}
private:
T value;
friend std::istream& operator>>(std::istream& is, MyValue& mv) {
return is >> mv.value;
}
friend std::ostream& operator<<(std::ostream& os, MyValue const& mv) {
return os << mv.value;
}
};
Now you could provide generic validators for any types in your MyLib namespace, be it optional or not, and have ADL find them through your MyLib namespace:
template <typename T, typename Values>
void validate(boost::any& v, Values const& values, T*, int) {
po::validators::check_first_occurrence(v);
v = boost::lexical_cast<T>(
po::validators::get_single_string(values));
}
template <typename T, typename Values>
void validate(boost::any& v, Values const& values, std::optional<T>*, int) {
po::validators::check_first_occurrence(v);
v = std::make_optional(
boost::lexical_cast<T>(
po::validators::get_single_string(values)));
}
} // namespace MyLib
See Live Demo
#include <boost/program_options.hpp>
#include <iostream>
#include <iomanip>
namespace po = boost::program_options;
namespace MyLib {
template <typename T> struct MyValue {
MyValue(T v = {}) : value(std::move(v)) {}
private:
T value;
friend std::istream& operator>>(std::istream& is, MyValue& mv) {
return is >> std::boolalpha >> mv.value;
}
friend std::ostream& operator<<(std::ostream& os, MyValue const& mv) {
return os << std::boolalpha << mv.value;
}
};
// Provide generic validators for any types in your MyLib namespace, be it
// optional or not
template <typename T, typename Values>
void validate(boost::any& v, Values const& values, T*, int) {
po::validators::check_first_occurrence(v);
v = boost::lexical_cast<T>(
po::validators::get_single_string(values));
}
template <typename T, typename Values>
void validate(boost::any& v, Values const& values, std::optional<T>*, int) {
po::validators::check_first_occurrence(v);
v = std::make_optional(
boost::lexical_cast<T>(
po::validators::get_single_string(values)));
}
} // namespace MyLib
int main(int ac, char* av[]) {
try {
using Int = MyLib::MyValue<int>;
using OptInt = std::optional<MyLib::MyValue<int>>;
using OptStr = std::optional<MyLib::MyValue<std::string> >;
po::options_description desc("Allowed options");
desc.add_options()
("ival", po::value<Int>()->default_value(Int{10}),
"integer value")
("opti", po::value<OptInt>()->default_value(OptInt{}, "(nullopt)"),
"optional integer value")
("sval", po::value<OptStr>()->default_value(OptStr{"secret"}, "'secret'"),
"optional string value")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
std::cout << "Options: " << desc << "\n";
if (vm.contains("ival")) {
std::cout << "ival is " << vm["ival"].as<Int>() << "\n";
}
if (vm.contains("opti")) {
if (auto& v = vm["opti"].as<OptInt>())
std::cout << "opti is " << v.value() << "\n";
else
std::cout << "opti is nullopt\n";
}
if (vm.contains("sval")) {
if (auto& v = vm["sval"].as<OptStr>())
std::cout << "sval is " << v.value() << "\n";
else
std::cout << "sval is nullopt\n";
}
} catch (std::exception& e) {
std::cout << e.what() << "\n";
return 1;
}
}
For ./a.out --ival=42 --sval=LtUaE prints:
Options: Allowed options:
--ival arg (=10) integer value
--opti arg (=(nullopt)) optional integer value
--sval arg (='secret') optional string value
ival is 42
opti is nullopt
sval is LtUaE
¹ see also See also Why Does Boost Use a Global Function Override to Implement Custom Validators in "Program Options"
How to pass lambda as template parameter.
For example this code
template<void (*callback)()>
void function() {
callback();
}
int main() {
function<[]() -> void { std::cout << "Hello world\n"; }>();
}
fails with error "invalid template argument for 'function', expected compile-time constant expression".
What I'm doing wrong.
Edit
I want to implement something like this
template<typename T,
T (*deserializer)(buffer *data),
void (*serializer)(T item, buffer *data)>
class Type {
public:
T item;
Type(T item) : item(item) {
}
Type(buffer *data) {
deserialize(data);
}
void serialize(buffer *data) {
serializer(item, data);
}
void deserialize(buffer *data) {
deserializer(data);
}
};
typedef Type<int, [](buffer* data) -> int { return -1; }, [](int item, buffer* data) -> void {}> IntType
typedef Type<long, [](buffer* data) -> long { return -1; }, [](long item, buffer* data) -> void {}> LongType
Lambdas in C++14, including their conversion to function pointers, are not constexpr.
In C++17, this is going to change. There are no stable compilers with that feature implemented that I'm aware of (if you find one, can you mention it in the comments below?).
At that point
constexpr auto tmp = []() -> void { std::cout << "Hello world\n"; };
function<+tmp>();
will definitely work. I am uncertain if
function<+[]() -> void { std::cout << "Hello world\n"; }>()
would work; there are some rules about lambdas in unevaluated contexts and inside template argument lists that may be separate from the constexpr lambda problem and may apply here.
We can hack it in C++14.
Create a template class that stores a static copy of a lambda and exposes a static function with the same signature (f_ptr) that calls that static copy of a lambda.
Instantiate it once globally with your lambda.
Pass a pointer to the f_ptr to your template.
So:
template<class L> struct stateless; // todo
template<class L> stateless<L> make_stateless(L l){return std::move(l);}
auto foo = make_stateless( []() -> void { std::cout << "Hello world\n"; } );
function< &foo::f_ptr >();
this is almost certainly not what you want.
The kind of template in the example does not take a type as a parameter, but rather a value. This value needs to be determinable at runtime, in order to instantiate the template, and the value of a lambda is not a compile time constant, so this method just wont do. The common way of sending functors to a function is:
template<typename Func>
void foo(Func&& f)
{
f();
}
And since you want a class template (please put such information in the question, not the comments), here is an example using classes:
#include <utility>
template<typename Func>
class MyClass
{
public:
MyClass(Func&& f) : f(f) {}
void Run() { f(); }
private:
Func f;
};
template<typename Func>
MyClass<Func> MakeMyClass(Func&& f)
{
return { std::forward<Func>(f) };
}
int main()
{
auto x = MakeMyClass( [](){} );
x.Run();
}
I'm not sure of the title, because I'm not sure the issue comes from the "copyablility" of my container.
I tryied quite everything but I can't get rid of this error.
Here is a simplified version of my code (please do not challenge the class design, I really would like to keep the end-used syntax in the BOOST_FOREACH):
template <typename T>
class MyContainer
{
public:
typedef typename std::vector<T>::iterator iterator;
typedef typename std::vector<T>::const_iterator const_iterator;
MyContainer(std::vector<T>& vec, boost::mutex& mutex) :
m_vector(vec),
m_lock(mutex)
{
}
iterator begin() { return m_vector.begin(); }
const_iterator begin() const { return m_vector.begin(); }
iterator end() { return m_vector.end(); }
const_iterator end() const { return m_vector.end(); }
private:
std::vector<T>& m_vector;
boost::lock_guard<boost::mutex> m_lock;
};
template <typename T>
struct GetContainer
{
GetContainer(std::vector<T>& vec, boost::mutex& mutex) :
m_vector(vec),
m_mutex(mutex)
{
}
MyContainer<T> Get()
{
return MyContainer<T>(m_vector, m_mutex);
}
std::vector<T>& m_vector;
boost::mutex& m_mutex;
};
int main()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
boost::mutex m;
GetContainer<int> getter(v, m);
BOOST_FOREACH(int i, getter.Get())
{
std::cout << i << std::endl;
}
return 0;
}
The compiler complains about not having a copy constructor for MyContainer::MyContainer(const MyContainer&).
I also have :
error: no matching function for call to ‘MyContainer::MyContainer(boost::foreach_detail_::rvalue_probe >::value_type)’
I follow the extensibility tips:
http://www.boost.org/doc/libs/1_58_0/doc/html/foreach/extensibility.html#foreach.extensibility.making__literal_boost_foreach__literal__work_with_non_copyable_sequence_types
But, making
MyContainer<T> : private boost::noncopyable
doesn't solve the issue.
Nor defining the function
boost_foreach_is_noncopyable
or specializing the template struct
is_noncopyable
for MyContainer (in fact, how would I specialize this template for a template type ?)
Last "tip":
If I remove the mutex and the lock from everywhere (I just pass the vector to GetContainer and to MyContainer), it works.
But it doesn't work if I make
MyContainer<T> : private boost::noncopyable
(I expected it should, so I'm not sure my problem is with BOOST_FOREACH, but maybe because I return a copy of MyContainer with my getter ?)
I thank you if you read me until here, and thanks in advance for help.
Seems to be a limitation of BOOST_FOREACH with move-only types. I didn't find a way around it¹ (except for the - ugly - obvious approach to put the lock_guard in a shared_ptr).
You didn't specify a c++03 requirement, though, so you can make it work without BOOST_FOREACH by replacing lock_guard with unique_lock.
Here's my take on things in c++11 (note how generic it is):
Live On Coliru
#include <boost/thread.hpp>
#include <boost/range.hpp>
namespace detail {
template <typename R, typename M>
struct RangeLock {
RangeLock(R&r, M& m) : _r(r), _l(m) {}
RangeLock(RangeLock&&) = default;
using iterator = typename boost::range_iterator<R>::type;
iterator begin() { using std::begin; return begin(_r); }
iterator end () { using std::end; return end (_r); }
using const_iterator = typename boost::range_iterator<R const>::type;
const_iterator begin() const { using std::begin; return begin(_r); }
const_iterator end () const { using std::end; return end (_r); }
private:
R& _r;
boost::unique_lock<M> _l;
};
}
template <typename R, typename M>
detail::RangeLock<R,M> make_range_lock(R& r, M& mx) { return {r,mx}; }
template <typename R, typename M>
detail::RangeLock<R const,M> make_range_lock(R const& r, M& mx) { return {r,mx}; }
#include <vector>
#include <map>
int main() {
boost::mutex mx;
std::vector<int> const vec { 1, 2 };
std::map<int, std::string> const map { { 1, "one" }, { 2, "two" } };
for(int i : make_range_lock(vec, mx))
std::cout << i << std::endl;
for(auto& p : make_range_lock(map, mx))
std::cout << p.second << std::endl;
for(auto& p : make_range_lock(boost::make_iterator_range(map.equal_range(1)), mx))
std::cout << p.second << std::endl;
}
Prints
1
2
one
two
one
¹ not even using all the approaches from Using BOOST_FOREACH with a constant intrusive list
I post my answer if it can help...
With C++03, I finally provide a copy constructor to be able to use the class with BOOST_FOREACH.
So the issue is moved to another topic: make the class copied in a logic and suitable way.
In my case, I "share the lock and the vector", the user shouldn't use this copy itself if he doesn't want to do bugs, but in BOOST_FOREACH it's okay:
I change the mutex to a recursive_mutex
I change the lock to an unique_lock
and:
MyContainer(const MyContainer& other) :
m_vector(other.vec),
m_lock(*other.m_lock.mutex())
{
}
With C++11
Thanks to Chris Glover on the boost mailling list, a C++11 solution:
You can't do what you are trying to do in C++03. To accomplish it, you
need C++11 move semantics to be able to move the MyContainer out of the Get
function. Even without using BOOST_FOREACH, the following code fails;
GetContainer<int> getter(v, m);
MyContainer<int> c = getter.Get(); // <-- Error.
Here's an example with the necessary changes; I changed the scoped_lock to
a unique_lock and added a move constructor.
template <typename T>
class MyContainer
{
public:
[...]
MyContainer(MyContainer&& other)
: m_vector(other.m_vector)
{
m_lock = std::move(other.m_lock);
other.m_vector = nullptr;
}
With some code left out, elsewhere on SOF there is code that looks like this:
// CRTP Abstract Base class for implementing static subject.
// Example Subclass Usage -- Printing Observer:
class Printer : public Observer<Printer> {
public:
Printer() : timesTriggered_(0) {}
template <typename... Args>
void OnNotify(Pressure<Args...> &subject, EventType event) {
std::cout << "Observer ID: " << this->GetID() << std::endl;
switch (event) {
case EventType::UNKNOWN: {
std::cout << "Unknown Event -- Event #" << timesTriggered_++
<< std::endl;
std::cout << "Pressure: " << subject.GetPressure() << std::endl;
break;
}
default: { break; }
}
}
private:
int timesTriggered_;
};
// CRTP Abstract Base class for implementing static subject.
// Example Subclass Usage -- Pressure Sensor:
template <typename... Obs>
class Pressure : public Subject<Pressure<Obs...>, Obs...> {
public:
typedef Subject<Pressure<Obs...>, Obs...> BaseType;
Pressure(std::tuple<Obs &...> &&observers, int pressure)
: BaseType(std::move(observers)), pressure_(pressure) {}
void Change(int value) {
pressure_ = value;
this->NotifyAll(EventType::UNKNOWN);
}
int GetPressure() const { return pressure_; }
private:
int pressure_;
};
// Binding function for use with MakeSubject
// Arguments: observer objects to observe subject notifications
// Return: tuple of references to observers
template <typename... Obs> std::tuple<Obs &...> BindObservers(Obs &... obs) {
return std::tuple<Obs &...>(obs...);
}
// Creator to ease subject creation
// Template Arguments: Subject subclass type
// Arguments: Result from BindObservers
// Any constructor arguments for Subject subclass
// Return: Subject subclass
// Example Usage:
// auto pressure = MakeSubject<Pressure>(BindObservers(printerObs), initialPressure);
template <template <typename...> class T, typename... Args, typename... Obs>
T<Obs...> MakeSubject(std::tuple<Obs &...> &&obs, Args &&... args) {
return T<Obs...>(std::move(obs), args...);
}
In main.cpp
int main() {
Printer printerObs1;
Printer printerObs2;
const int initialPressure = 1;
auto pressure = MakeSubject<Pressure>(
BindObservers(printerObs1, printerObs2), initialPressure);
pressure.Change(12);
}
I need to break out the BindObservers and the return type of MakeSubject, but I can't correctly figure out what to replace both **auto in the pseudo-code below:**
auto obs = BindObservers(printerObs1, printerObs2);
auto pressure = MakeSubject<Pressure>(obs, initialPressure);
What is the exapanded version return types of both auto above? I need to store the return values in std::vector and AFAIK, I can't say
std::vector<auto> vec
[Although I don't see why not since the compiler can probably figure it out]
You can use std::vector<decltype(pressure)>.
But the type should be Pressure<Printer, Printer>.
Can I have class/struct with rvalue field in c++11?
Like this one:
template<typename T>
struct RvalueTest{
RvalueTest(T&& value) : value( std::forward<T>(value) ){}
T&& value;
};
Because in the following test:
class Widget {
public:
Widget(){std::cout << "Widget ctor " << std::endl; }
Widget(int h) : h(h){std::cout << "Widget ctor param " << std::endl; }
Widget(const Widget&) { std::cout << "Widget copy ctor " << std::endl; }
Widget(Widget&&) { std::cout << "Widget move ctor " << std::endl; } // added this
template<typename T>
Widget(const T&) { std::cout << "Generalized Widget copy ctor " << std::endl; }
template<typename T>
Widget(T&&) { std::cout << "Universal Widget ctor " << std::endl; }
int h;
};
RvalueTest<Widget> r(Widget(12));
std::cout << r.value.h;
I got some trash value at output (with -O2):
http://coliru.stacked-crooked.com/a/7d7bada1dacf5352
Widget ctor param
4203470
And right value with -O0:
http://coliru.stacked-crooked.com/a/f29a8469ec179046
Widget ctor param
12
WHY???
P.S. What I try to achive is a single ctor call, without any additional move/copy constructors.
UPDATED
It compiles ok with clang http://coliru.stacked-crooked.com/a/6a92105f5f85b943
GCC bug? Or it works as it should ?
The problem is not specific to rvalue references. You will see the same odd behaviour, if you replace T&& by const T&.
template<typename T>
struct ReferenceTest{
ReferenceTest(const T& value) : value(value){}
const T& value;
};
The problem is, that you are storing a reference to a temporary object. The temporary object is automatically destroyed at the end of the statement it was created, but you try to access it later via the reference.
RvalueTest<Widget> r(Widget(12)); // pass reference to temporary object to constructor
std::cout << r.value.h; // invalid reference
It is possible to use rvalue references as member variables. However, you have to be careful if doing so. An example from the standard library is std::forward_as_tuple.
auto t = std::forward_as_tuple(42);
// t has type std::tuple<int&&>
Regarding the question how to avoid the copy or move construction: Don't use rvalue reference member variables. Here are two approaches, you can use instead:
std::unique_ptr: If copying or moving a Widget is too expensive, you can use a smart pointer:
template <typename T>
struct Test
{
std::unique_ptr<T> _value;
explicit Test(std::unique_ptr<T> value) : _value{std::move(value)} { }
};
Test<Widget> t{std::make_unique<Widget>(12)};
std::cout << t._value->h;
The second approach creates the Widget in-place. That's what the different emplace functions of the standard containers do, e.g. std::vector::emplace_back.
template <typename T>
struct Test
{
T _value;
template <typename ...Args>
explicit Test(Args&&... args) : _value{std::forward<Args>(args)...} { }
};
Test<Widget> t{12};
std::cout << t._value.h;