I have some struct like this:
struct A { ... };
struct B { ... };
And I have a template like this:
template<typename struct_arg>
class X { ... }
Now I wanna create an array of arguments as struct like this:
args [2] { A, B };
for (args) {
X<args[i]> x;
}
Can I possible to create an array like this!?
Yes, you can do. std::variant is made for this usage and you can access such members of a variant with std::visit.
But if you do so, keep in mind, that each element of the array has an additional data member which has the type information and that each element has at minimum the size of the largest type you store. And also std::visit comes with a overhead, as a table for access the data member must be created. Typically done in compile time, but sometime g++ generates it in run time, which will decrease speed a lot!
struct A
{
void Do() { std::cout << "A" << std::endl; }
};
struct B
{
void Do() { std::cout << "B" << std::endl; }
};
int main()
{
std::array<std::variant< A,B >,2> arr{ A{}, B{}, B{}, A{} };
for ( auto& element: arr )
{
std::visit( []( auto& vari ) { vari.Do(); }, element );
}
}
Or if you like your encapsulation with an additional strucuture/class like given in your example:
struct A
{
void Do() { std::cout << "A" << std::endl; }
};
struct B
{
void Do() { std::cout << "B" << std::endl; }
};
template < typename struct_arg >
struct X: public struct_arg{};
int main()
{
std::array<std::variant< X<A>,X<B> >,2> arr{ X<A>{}, X<B>{} };
for ( auto& element: arr )
{
std::visit( []( auto& vari ) { vari.Do(); }, element );
}
}
Related
struct Base
{
Base(int a_) : a(a_)
{
}
int a;
};
class Derived: public Base
{
public:
Derived(int i): Base(i)
{
}
Derived(const Derived && rhs)
: Base(std::move(rhs))
{
}
};
int main()
{
Derived d1(2);
Derived d2 = std::move(d1);
std::cout << d1.a << '\n';
}
Why the thing from which we just moved, that is, the thing that we just pilfered, is still accessible on subsequent lines of code? I mean d1.a.
https://stackoverflow.com/a/31860104
#include <iostream>
#include <string>
template<class T>
auto optionalToString(T* obj)
-> decltype( obj->toString() )
{
return obj->toString();
}
auto optionalToString(...) -> std::string
{
return "toString not defined";
}
struct TA
{
std::string toString() const
{
return "Hello";
}
};
struct TB
{
};
Question> Given the proposed solution optionalToString, how I can use it to detect that TA has toString while TB doesn't.
A solution using can_apply from this code:
template<class T>
using toString_result = decltype(std::declval<T>().toString());
template<class T>
constexpr auto has_toString = can_apply<toString_result, T>::value;
Used like this:
struct TA
{
std::string toString() const
{
return "Hello";
}
};
struct TB
{
};
int main()
{
std::cout << has_toString<TA> << '\n';
std::cout << has_toString<TB> << '\n';
return 0;
}
DEMO
The given solution allows you to always get a string from any object. If it has a toString() member function, this will be used, otherwise, a default string. Usage example, given the above:
TA a;
TB b;
std::cout << "a: " << optionalToString(&a) << '\n';
std::cout << "b: " << optionalToString(&b) << std::endl;
However, you will not get a boolean value whether a or b has a toString() method. If you want that, you need something like the solution proposed by O'Neil.
After many trials I still do not understand how to properly take advantage of the move semantics in order to not copy the result of the operation and just use the pointer, or std::move, to "exchange" the data pointed to. This will be very usefull to speed-up more complicated functions like f(g(),h(i(l,m),n(),p(q()))
The objective is to have:
t3={2,4,6};
t1={}; // empty
While executing the code below the output is:
t3={2,4,6};
t1={1,2,3};
Code:
namespace MTensor {
typedef std::vector<double> Tensor1DType;
class Tensor1D {
private:
//std::shared_ptr<Tensor1DType> data = std::make_shared<Tensor1DType>();
Tensor1DType * data = new Tensor1DType;
public:
Tensor1D() {
};
Tensor1D(const Tensor1D& other) {
for(int i=0;i<other.data->size();i++) {
data->push_back(other.data->at(i));
}
}
Tensor1D(Tensor1D&& other) : data(std::move(other.data)) {
other.data = nullptr;
}
~Tensor1D() {
delete data;
};
int size() {
return data->size();
};
void insert(double value) {
data->push_back(value);
}
void insert(const std::initializer_list<double>& valuesList) {
for(auto value : valuesList) {
data->push_back(value);
}
}
double operator() (int i) {
if(i>data->size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
return data->at(i);
}
Tensor1D& operator=(Tensor1D&& other) {
if (this == &other){
return *this;
}
data = other.data;
other.data = nullptr;
return *this;
}
void printTensor(Tensor1DType info) {
for(int i=0;i<info.size();i++) {
std::cout << info.at(i) << "," << std::endl;
}
}
void printTensor() {
for(int i=0;i<data->size();i++) {
std::cout << data->at(i) << "," << std::endl;
}
}
};
} // end of namespace MTensor
In file main.cpp:
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
MTensor::Tensor1D tensor;
for(int i=0;i<t1.size();++i) {
tensor.insert(t1(i) * scalar);
}
//return std::move(tensor);
return tensor;
}
int main() {
MTensor::Tensor1D t1;
t1.insert({1,2,3});
std::cout << "t1:" << std::endl;
t1.printTensor();
MTensor::Tensor1D t3(scalarProduct1D(t1,2));
std::cout << "t3:" << std::endl;
t3.printTensor();
std::cout << "t1:" << std::endl;
t1.printTensor();
return 0;
}
Your use of new is a red flag, especially on a std::vector.
std::vectors support move semantics natively. They are a memory management class. Manual memory management of a memory management class is a BIG red flag.
Follow the rule of 0. =default your move constructor, move assignment, copy constructor, destructor and copy assignment. Remove the * from the vector. Don't allocate it. Replace data-> with data.
The second thing you should do is change:
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
As it stands you take the first argument by value. That is great.
But once you take it by value, you should reuse it! Return t1 instead of creating a new temporary and returning it.
For that to be efficient, you will want to have a way to modify a tensor in-place.
void set(int i, double v) {
if(i>data->size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
data.at(i) = v;
}
which gives us:
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
for(int i=0;i<t1.size();++i) {
ts.set(i, t1(i) * scalar);
}
return t1; // implicitly moved
}
We are now getting close.
The final thing you have to do is this:
MTensor::Tensor1D t3(scalarProduct1D(std::move(t1),2));
to move the t1 into the scalarProduct1D.
A final problem with your code is that you use at and you check bounds. at's purpose is to check bounds. If you use at, don't check bounds (do so with a try/catch). If you check bounds, use [].
End result:
typedef std::vector<double> Tensor1DType;
class Tensor1D {
private:
//std::shared_ptr<Tensor1DType> data = std::make_shared<Tensor1DType>();
Tensor1DType data;
public:
Tensor1D() {};
Tensor1D(const Tensor1D& other)=default;
Tensor1D(Tensor1D&& other)=default;
~Tensor1D()=default;
Tensor1D& operator=(Tensor1D&& other)=default;
Tensor1D& operator=(Tensor1D const& other)=default;
Tensor1D(const std::initializer_list<double>& valuesList) {
insert(valuesList);
}
int size() const {
return data.size();
};
void insert(double value) {
data.push_back(value);
}
void insert(const std::initializer_list<double>& valuesList) {
data.insert( data.end(), valuesList.begin(), valuesList.end() );
}
double operator() (int i) const {
if(i>data.size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
return data[i];
}
void set(int i, double v) {
if(i>data->size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
data.at(i) = v;
}
static void printTensor(Tensor1DType const& info) {
for(double e : info) {
std::cout << e << "," << std::endl;
}
}
void printTensor() const {
printTensor(data);
}
};
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
for(int i=0;i<t1.size();++i) {
t1.set(i, t1(i) * scalar);
}
return t1;
}
int main() {
MTensor::Tensor1D t1 = {1,2,3};
std::cout << "t1:" << std::endl;
t1.printTensor();
MTensor::Tensor1D t3(scalarProduct1D(std::move(t1),2));
std::cout << "t3:" << std::endl;
t3.printTensor();
std::cout << "t1:" << std::endl;
t1.printTensor();
return 0;
}
with a few other minor fixes (like using range-for, DRY, etc).
You need to move t1 when calling scalarProduct1D, otherwise you'll make a copy:
MTensor::Tensor1D t3(scalarProduct1D(std::move(t1),2));
You need to explicitly use std::move because t1 is an lvalue expression.
Note that you'll have to fix your printing functions to avoid dereferencing nullptr if you want accessing the moved-from object to be a valid operation. I instead suggest to avoid making method invocation on moved-from objects valid as it requires additional checks and doesn't follow the idea of "this object has been moved, now it's in an invalid state".
live wandbox example
I'm trying to use a std::multiset container with strict weak ordering on the EdgeProperties for the EdgeList template parameter of boost::adacency_list
namespace boost {
struct propOrderedMultisetS { };
template <class ValueType>
struct container_gen<propOrderedMultisetS,ValueType> {
struct less {
bool operator() (const ValueType& lhs, const ValueType& rhs) const {
return (lhs.get_property() < rhs.get_property());
};
};
typedef std::multiset<ValueType, less> type;
};
struct MyVertexProp { int v; };
struct MyEdgeProp {
bool operator<(const MyEdgeProp& rhs) const {
return this->weight < rhs.weight;
}
double weight;
}
typedef adjacency_list<listS, listS, undirectedS, MyVertexProp, MyEdgeProp,
no_property, propOrderedMultisetS> PropOrderedGraph;
}
using namespace boost;
int main() {
PropOrderedGraph g;
// ... adding some vertices and edges
for (auto e_range=edges(g); e_range.first != e_range.second; ++e_range.first) {
// works! prints the edges ordered by weight
std::cout << g[*e_range.first].weight << std::endl;
}
for (auto v_range=vertices(g); v_range.first != v_range.second; ++v_range.first) {
// works! prints all vertices (random order)
std::cout << g[*v_range.first].v << std::endl;
}
auto first_vertex = *vertices(g).first;
for (auto adj_v_range=adjacent_vertices(first_vertex, g); adj_v_range.first != adj_v_range.second; ++adj_v_range.first) {
// problem: dereferencing causes compiler error, see below
std::cout << g[*adj_v_range.first].v << std::endl;
}
return 0;
}
Dereferencing the iterator in the third for-loop causes a compiler error:
/usr/include/boost/graph/detail/adjacency_list.hpp:293:69: error: invalid initialization of reference of type ‘MyEdgeProp&’ from expression of type ‘const MyEdgeProp’
inline Property& get_property() { return m_iter->get_property(); }
Any ideas how I could fix that error or how else I could accomplish the task?
I changed the graph library and now am using LEMON, which provides a class IterableValueMap for my task.
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>.