The following code yields warning in G++:
#include <iostream>
#include <cstdint>
template <typename T, typename P, typename Q>
Q T::*pointer_to(P T::*p, Q P::*q)
{
typedef Q T::* output_ptr;
// warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
size_t tmp = reinterpret_cast<const size_t&>(p) + reinterpret_cast<const size_t&>(q);
return reinterpret_cast<const output_ptr&>(tmp);
}
struct A { int x; };
struct B { A a; };
int main()
{
B b = B();
b.*pointer_to(&B::a, &A::x) = 1;
std::cout << b.a.x << std::endl;
}
It works properly anyway, but that makes me worry.
What is your opinion, are these "sub-member" pointers susceptible to extra strict aliasing issues than plain member pointers?
I would recommend against doing it this way.
You stated in your comments that you tried using a nested std::bind, but there's an issue with the compiler version you're using. Rather than resort to the hack, I would roll my own repeated pointer to member class.
#include <iostream>
#include <cstdint>
#include <type_traits>
#include <utility>
template<typename Ptr1, typename... Rest>
class pointer_to_sub;
template<typename ObjType, typename Class>
class pointer_to_sub<ObjType Class::* >
{
typedef ObjType Class::* ptr_type;
public:
typedef ObjType value_type;
typedef Class input_type;
pointer_to_sub(ptr_type input) : ptr(input)
{
}
value_type& operator()(input_type& from) const
{
return from.*ptr;
}
value_type const& operator()(input_type const& from) const
{
return from.*ptr;
}
value_type& operator()(input_type* from) const
{
return from->*ptr;
}
value_type const& operator()(input_type const* from) const
{
return from->*ptr;
}
private:
ptr_type ptr;
};
template<typename ObjType, typename Class, typename... Rest >
class pointer_to_sub<ObjType Class::*, Rest...> : private pointer_to_sub<Rest...>
{
typedef ObjType Class::* ptr_type;
typedef pointer_to_sub<Rest...> base_type;
public:
typedef typename base_type::value_type value_type;
typedef Class input_type;
pointer_to_sub(ptr_type input, Rest... args) : base_type(args...), ptr(input)
{
}
value_type& operator()(input_type& from) const
{
return base_type::operator()(from.*ptr);
}
value_type const& operator()(input_type const& from) const
{
return base_type::operator()(from.*ptr);
}
value_type& operator()(input_type* from) const
{
return base_type::operator()(from->*ptr);
}
value_type const& operator()(input_type const* from) const
{
return base_type::operator()(from->*ptr);
}
private:
ptr_type ptr;
};
template<typename T, typename... Args>
pointer_to_sub<T, Args...> make_pointer_to_sub(T t1, Args... args)
{
return pointer_to_sub<T, Args...>(t1, args...);
}
The above basically provides a make_pointer_to_sub which takes a list of member object pointers. It accepts as its input a reference or a pointer that's convertible to the first type, and then dereferences each of the pointers in turn. It could be improved to accept unique_ptr or shared_ptr, but that's for later. You use it as seen below.
struct A { int x; double y;};
struct B { A a; };
int main()
{
auto ptr = make_pointer_to_sub(&B::a, &A::x);
B b = B();
ptr(b) = 1;
// b.*pointer_to(&B::a, &A::x) = 1;
std::cout << b.a.x << std::endl;
ptr(&b) = 2;
std::cout << b.a.x << std::endl;
}
If you needed to, this could be assigned to a std::function with the appropriate arguments.
Related
I am trying to implement an std::unordered_map that returns pairs of either double, int or std::string. The keys for the map are std::strings. Below is what I have tried so far:
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <unordered_map>
#include <utility>
#include <vector>
// A base class for boundary class
class Boundbase {
public:
Boundbase(){};
virtual ~Boundbase(){};
};
// A different map of boundaries for each different data type
template <class dType>
class Boundary : public Boundbase {
std::pair<dType, dType> bpair;
public:
//Constructor
Boundary(const std::string &lbound,
const std::string &ubound) {
setbound(lbound, ubound);
};
//A method to set boundary pair
void setbound(const std::string &lbound,
const std::string &ubound);
// A method to get boundary pair
std::pair<dType, dType> getbound() {return bpair;}
};
// Class to hold the different boundaries
class Boundaries {
std::unordered_map<std::string, Boundbase*> bounds;
public:
//Constructor
Boundaries() {};
// A method to set boundary map
void setboundmap(std::unordered_map<std::string,
std::vector<std::string>> xtb);
// A template to get boundaries.
std::unordered_map<std::string, Boundbase*> getbounds()
{return bounds;}
};
// A method to set covariate boundary
template <class dType> void
Boundary<dType>::setbound(const std::string &lbound,
const std::string &ubound) {
dType val;
std::istringstream isa(lbound);
while(isa >> val) {
bpair.first = val;
}
std::istringstream isb(ubound);
while(isb >> val) {
bpair.second = val;
}
}
// A method to set boundary map
void Boundaries::setboundmap(std::unordered_map<std::string,
std::vector<std::string>> xtb) {
for(auto s : xtb) {
char type = s.second[1][0];
switch(type) {
case 'd': {
std::pair<std::string, Boundbase*> opair;
opair.first = s.first;
opair.second = new Boundary<double>(
s.second[2], s.second[3]);
bounds.insert(opair);
}
break;
case 'i': {
std::pair<std::string, Boundbase*> opair;
opair.first = s.first;
opair.second = new Boundary<int>(
s.second[2], s.second[3]);
bounds.insert(opair);
break;
}
case 'c': {
std::pair<std::string, Boundbase*> opair;
opair.first = s.first;
opair.second = new Boundary<std::string>(
s.second[2], s.second[2]);
bounds.insert(opair);
break;
}
}
}
}
This compiles ok using g++. When I try to run it though ( as follows):
int main() {
Data D;
Boundaries B;
std::ifstream iss("tphinit.txt");
D.read_lines(iss);
auto dbounds = D.get_xtypebound();
B.setboundmap(dbounds);
auto tbounds = B.getbounds();
auto sbound = tbounds["X1"];
std::cout << sbound->bpair.first << ","
<< sbound->bpair.second << std::endl;
}
I get 'class Boundbase' has no member named 'bpair' which is true because I am pointing to the base class and not the derived class. As far as I can tell, trying to get the derived member bpair requires that I use the visitor pattern. Now, it is clear that I am noob so when I had a look at different ways of doing this on SO I was a little in over my head (no reflection on the authors, just on my inexperience).
So my main question is: Is this the best and simplest way to go about this? I would like to avoid boost::variant if at all possible (mainly for the sake of purity: this cannot be that difficult). A sub-question is whether I have to use the visitor pattern or is there a better/simpler way to get the member pbair?
I will have to perform this lookup many times so I am hoping to make it as fast as possible but using the stl for the sake of simplicity.
Make your values std variants over the 3 types.
Failing that, boost variant.
Std and boost variant really are what you want. You'll end up implementing some subset of its implementation.
Failing that, find a tutorial on how to implement ones of them, or use std any. Failing that, dynamic casts around an otherwise useless wrapper type with a virtual dtor stored in a unique ptr, or do manual RTTI with try get methods.
This just gets increasingly ugly and/or inefficient however.
Boost variant, and std variant from it, was implemented for a reason, and that reason was solving the exact problem you are describing in an efficient manner.
#include <tuple>
#include <utility>
#include <string>
template<class...Ts>
struct destroy_helper {
std::tuple<Ts*...> data;
destroy_helper( std::tuple<Ts*...> d ):data(d){}
template<class T>
static void destroy(T* t){ t->~T(); }
template<std::size_t I>
void operator()(std::integral_constant<std::size_t, I>)const {
destroy( std::get<I>( data ) );
}
};
struct construct_helper {
template<class T, class...Args>
void operator()(T* target, Args&&...args)const {
::new( (void*)target ) T(std::forward<Args>(args)...);
}
};
template<std::size_t...Is>
struct indexes {};
template<std::size_t N, std::size_t...Is>
struct make_indexes:make_indexes<N-1, N-1, Is...> {};
template<std::size_t...Is>
struct make_indexes<0, Is...>{
using type=indexes<Is...>;
};
template<std::size_t N>
using make_indexes_t = typename make_indexes<N>::type;
template<class F>
void magic_switch( std::size_t i, indexes<>, F&& f ) {}
template<std::size_t I0, std::size_t...Is, class F>
void magic_switch( std::size_t i, indexes<I0,Is...>, F&& f )
{
if (i==I0) {
f( std::integral_constant<std::size_t, I0>{} );
return;
}
magic_switch( i, indexes<Is...>{}, std::forward<F>(f) );
}
template<class T0>
constexpr T0 max_of( T0 t0 ) {
return t0;
}
template<class T0, class T1, class...Ts>
constexpr T0 max_of( T0 t0, T1 t1, Ts... ts ) {
return (t1 > t0)?max_of(t1, ts...):max_of(t0, ts...);
}
template<class...Ts>
struct Variant{
using Data=typename std::aligned_storage< max_of(sizeof(Ts)...), max_of(alignof(Ts)...)>::type;
std::size_t m_index=-1;
Data m_data;
template<std::size_t I>
using alternative_t=typename std::tuple_element<I, std::tuple<Ts...>>::type;
using pointers=std::tuple<Ts*...>;
using cpointers=std::tuple<Ts const*...>;
template<class T> T& get(){ return *reinterpret_cast<T*>(&m_data); }
template<class T> T const& get() const { return *reinterpret_cast<T*>(&m_data); }
template<std::size_t I>
alternative_t<I>& get(){ return std::get<I>(get_pointers()); }
template<std::size_t I>
alternative_t<I> const& get()const{ return std::get<I>(get_pointers()); }
pointers get_pointers(){
return pointers( (Ts*)&m_data... );
}
cpointers get_pointers()const{
return cpointers( (Ts const*)&m_data... );
}
std::size_t alternative()const{return m_index;}
void destroy() {
if (m_index == -1)
return;
magic_switch(m_index, make_indexes_t<sizeof...(Ts)>{}, destroy_helper<Ts...>(get_pointers()));
}
template<std::size_t I, class...Args>
void emplace(Args&&...args) {
destroy();
construct_helper{}( std::get<I>(get_pointers()), std::forward<Args>(args)... );
m_index = I;
}
Variant()=default;
Variant(Variant const&)=delete;//todo
Variant&operator=(Variant const&)=delete;//todo
Variant(Variant &&)=delete;//todo
Variant&operator=(Variant &&)=delete;//todo
~Variant(){destroy();}
};
int main() {
Variant<int, double, std::string> bob;
bob.emplace<0>( 7 );
bob.emplace<1>( 3.14 );
bob.emplace<2>( "hello world" );
}
here is a really simple variant interface.
The hard part is turning a runtime index into which of the compile time indexes you want to use. I call that the magic switch problem.
You might also want to implement apply visitor.
...
Or...
template<class T>
struct Derived;
struct Base {
virtual ~Base() {}
template<class T>
friend T* get(Base* base) {
Derived<T>* self = dynamic_cast<T*>(base);
return self?&self.t:nullptr;
}
template<class T>
friend T const* get(Base const* base) {
Derived<T> const* self = dynamic_cast<T const*>(base);
return self?&self.t:nullptr;
}
};
template<class T>
struct Derived:Base {
Derived(T in):t(std::move(in)){}
T t;
};
std::unordered_map<std::string, std::unique_ptr<Base>> map;
map["hello"] = std::unique_ptr<Base>( new Derived<int>(-1) );
map["world"] = std::unique_ptr<Base>( new Derived<double>(3.14) );
int* phello = get<int>(map["hello"]);
if (phello) std::cout << *hello << "\n";
double* pworld = get<double>(map["world"]);
if (pworld) std::cout << *world << "\n";
which is a seriously bargain-basement std::any.
This is a follow-up to my previous question.
I have a class with a cast operator to anything. In a pre-C++17 environment this yields errors of being unable to select appropriate constructor overload while performing initialization. I want to tune the behavior by marking the cast operator explicit for some types. However, I cannot find a way to do so.
Here is an artificial example: I want an implicit cast operator to integer types and explicit to all other types.
This doesn't work because we cannot determine U having the expression of type typename std::enable_if<!std::is_integral<U>::value, U>::type:
struct C {
template<typename U>
operator typename std::enable_if< std::is_integral<U>::value, U>::type() const {
return 1;
}
template<typename U>
explicit operator typename std::enable_if<!std::is_integral<U>::value, U>::type() const {
return 1.5;
}
};
This one fails to compile saying that C::operator U() cannot be overloaded:
struct C {
template<typename U, typename = typename std::enable_if< std::is_integral<U>::value, U>::type>
operator U() const {
return 1;
}
template<typename U, typename = typename std::enable_if<!std::is_integral<U>::value, U>::type>
explicit operator U() const {
return 1.5;
}
};
I cannot declare the function of kind template<typename U, typename = void> operator U(); and partially specialize it because partial function specialization is not allowed and making a helper class looks like an overkill to me.
How can I declare cast operator based on some traits of the type I'm casting to?
I need a C++11 solution, as in C++17 the issue from my previous question is already resolved.b
You can move definitions of these operators to the base classes. This approach allows you put constraints on both implicit and explicit operators:
#include <type_traits>
#include <iostream>
template<typename TDerived> class
t_ImplicitlyConvertableToAnything
{
public: template
<
typename TTarget
, typename TEnabled = typename ::std::enable_if_t<::std::is_integral<TTarget>::value>
>
operator TTarget(void) const
{
auto const & self{static_cast<const TDerived &>(*this)};
return(self.template CheckedConversion_To_Integral<TTarget>());
}
};
template<typename TDerived> class
t_ExplicitlyConvertableToAnything
{
public: template
<
typename TTarget
, typename TEnabled = typename ::std::enable_if_t<!::std::is_integral<TTarget>::value>
> explicit
operator TTarget(void) const
{
auto const & self{static_cast<const TDerived &>(*this)};
return(self.template CheckedConversion_To_NonIntegral<TTarget>());
}
};
class
t_ConvertableToAnything
: public t_ImplicitlyConvertableToAnything<t_ConvertableToAnything>
, public t_ExplicitlyConvertableToAnything<t_ConvertableToAnything>
{
public: template<typename TTarget> decltype(auto)
CheckedConversion_To_Integral(void) const
{
return(static_cast<TTarget>(1));
}
public: template<typename TTarget> decltype(auto)
CheckedConversion_To_NonIntegral(void) const
{
return(static_cast<TTarget>(3.14));
}
};
int main()
{
t_ConvertableToAnything c;
::std::cout << ([](int x){return(x);})(c) << ::std::endl;
::std::cout << static_cast<float>(c) << ::std::endl;
return(0);
}
Run this code online
You can use non-type template parameters to avoid the "cannot be overloaded" issue:
#include <iostream>
#include <type_traits>
struct A { };
struct B { };
struct C {
template <typename U,
typename std::enable_if<std::is_integral<U>::value>::type* = nullptr>
explicit operator U() const {
return 1;
}
template<typename U,
typename std::enable_if<std::is_same<U, A>::value>::type* = nullptr>
explicit operator U() const {
return A{ };
}
template<typename U,
typename std::enable_if<std::is_same<U, B>::value>::type* = nullptr>
explicit operator U() const {
return B{ };
}
};
int main() {
C c;
long y = static_cast<int>(c);
B b = static_cast<B>(c);
A a = static_cast<A>(c);
}
https://ideone.com/smfPwF
You can overload your cast operator using a trick with dummy template parameters for disambiguation.
struct C {
template<typename U,
typename = typename enable_if<is_integral<U>::value, U>::type,
int = 0> // <== hete
operator U() const {
return 1;
}
template<typename U,
typename = typename enable_if<!is_integral<U>::value, U>::type,
char = 0> // <== and here
explicit operator U() const {
return 1.5;
}
};
Since the template signatures are now different, there is no ambiguity.
Try this. Just leave off the constraints on the explicit operator since it covers all cases that the first operator does not.
Coliru example: http://coliru.stacked-crooked.com/a/3d0bc6e59ece55cf
#include <iostream>
#include <type_traits>
struct C {
template <typename U,
typename = typename std::enable_if< std::is_integral<U>::value>::type>
operator U() const {
return 1;
}
template<typename U, typename std::enable_if<!std::is_integral<U>::value>::type* = nullptr>
explicit operator U() const {
return 1.5;
}
};
int main() {
C c;
int v = c;
int w = c;
int x = static_cast<int>(c);
long y = static_cast<int>(c);
double z = static_cast<double>(c);
std::cout << v << std::endl;
std::cout << w << std::endl;
std::cout << x << std::endl;
std::cout << y << std::endl;
std::cout << z << std::endl;
}
Thanks to #Jodocus for enabling explicit casts to integral types.
I have my Find method which I want to use both with shared and weak pointers. Live example
using namespace std;
template<typename value>
struct A
{
template < typename T, typename F >
T Find( F filterFunction)
{
for ( size_t i = 0; i < iteratableList.size(); i++)
{
auto castedTerrain = dynamic_pointer_cast<typename T::element_type>(iteratableList[i]);
if ( castedTerrain && filterFunction(castedTerrain) )
return iteratableList[i];
}
return T();
}
std::vector<value> iteratableList;
};
int main()
{
{
std::vector<std::shared_ptr<std::string>> names = { make_shared<std::string>("needle"), make_shared<std::string>("manyOtherNames") } ;
A<std::shared_ptr<std::string>> iterateable{ names };
iterateable.Find<std::shared_ptr<std::string>>([] ( std::shared_ptr<std::string> in ){ return *in == "needle";});
}
// When I use weak pointer my Find function fails.
//{
// std::vector<std::shared_ptr<std::string>> weakNames ;
// for ( auto elem : names )
// weakNames.push_back(elem)
// A<std::weak_ptr<std::string>> iterateable{ weakNames };
// iterateable.Find<std::weak_ptr<std::string>>([] ( std::weak_ptr<std::string> in ){ return *in == "needle";});
//}
}
I know I can do something like
std::is_same< std::weak_ptr ... > and use std::true_type and std::false_type but I am curious if there is a better and cleaner way to achieve avoid code duplication just for .lock() method.
Just have a template function you can use to obtain the "real" pointer. The specialization for std::shared_ptr just returns the argument:
template <typename T>
struct resolve_pointer;
template <typename T>
struct resolve_pointer<std::shared_ptr<T>>
{
static std::shared_ptr<T> resolve(std::shared_ptr<T> & p) const {
return p;
}
};
template <typename T>
struct resolve_pointer<std::weak_ptr<T>>
{
static std::shared_ptr<T> resolve(std::weak_ptr<T> & p) const {
return p.lock();
}
};
Now your Find function, in place of iteratableList[i], use resolve_pointer<T>::resolve(iteratableList[i]).
I'm a potato, an overloaded free function would work just as well and be a bit simpler to understand:
template <typename T>
std::shared_ptr<T> resolve_pointer(std::shared_ptr<T> & p) {
return p;
}
template <typename T>
std::shared_ptr<T> resolve_pointer(std::weak_ptr<T> & p) {
return p.lock();
}
If your goal is to be able to extend your code to provide interoperability with any strong/weak pointer pairs that have an implemented dynamic cast operation (shown here the std::strong/weak_ptr and boost::strong/weak_ptr), you can do this using a set of traits, like so... beware, dragons ahead:
// Defines a resolve static function to get a strong pointer from either
// a strong or a weak pointer.
template <typename T>
struct smart_pointer_info;
template <typename T>
struct smart_pointer_info<std::shared_ptr<T>>
{
typedef std::shared_ptr<T> ptr_type;
typedef T element_type;
typedef std::shared_ptr<T> resolved_type;
static resolved_type resolve(ptr_type & p) {
return p;
}
};
template <typename T>
struct smart_pointer_info<std::weak_ptr<T>>
{
typedef std::weak_ptr<T> ptr_type;
typedef T element_type;
typedef std::shared_ptr<T> resolved_type;
static resolved_type resolve(ptr_type & p) {
return p.lock();
}
};
template <typename T>
struct smart_pointer_info<boost::shared_ptr<T>>
{
typedef boost::shared_ptr<T> ptr_type;
typedef T element_type;
typedef boost::shared_ptr<T> resolved_type;
static resolved_type resolve(ptr_type & p) {
return p;
}
};
template <typename T>
struct smart_pointer_info<boost::weak_ptr<T>>
{
typedef boost::weak_ptr<T> ptr_type;
typedef T element_type;
typedef boost::shared_ptr<T> resolved_type;
static resolved_type resolve(ptr_type & p) {
return p.lock();
}
};
// Provides a static "cast" function that converts a strong pointer T
// into a strong point that points at an object of type D.
template <typename T, typename D>
struct smart_pointer_dynamic_cast;
template <typename T, typename D>
struct smart_pointer_dynamic_cast<std::shared_ptr<T>, D>
{
typedef std::shared_ptr<T> ptr_type;
typedef std::shared_ptr<D> cast_type;
static cast_type cast(ptr_type & p) {
return std::dynamic_pointer_cast<D>(p);
}
};
template <typename T, typename D>
struct smart_pointer_dynamic_cast<boost::shared_ptr<T>, D>
{
typedef boost::shared_ptr<T> ptr_type;
typedef boost::shared_ptr<D> cast_type;
static cast_type cast(ptr_type & p) {
return boost::dynamic_pointer_cast<D>(p);
}
};
// Helper so we can omit the template parameter for the source pointer type.
template <typename D>
struct dynamic_cast_helper
{
template <typename P>
static typename smart_pointer_dynamic_cast<typename smart_pointer_info<P>::resolved_type, D>::cast_type cast(P & p) {
typename smart_pointer_info<P>::resolved_type r = smart_pointer_info<P>::resolve(p);
return smart_pointer_dynamic_cast<typename smart_pointer_info<P>::resolved_type, D>::cast(r);
}
};
// Then we might use it like so:
class A {
public:
virtual void print() {
std::cout << "A::print()" << std::endl;
}
};
class B : public A {
public:
virtual void print() {
std::cout << "B::print()" << std::endl;
}
};
int main()
{
auto x = std::make_shared<B>();
std::weak_ptr<B> xw{x};
auto y = boost::make_shared<B>();
boost::weak_ptr<B> yw{y};
dynamic_cast_helper<A>::cast(x)->print();
dynamic_cast_helper<A>::cast(xw)->print();
dynamic_cast_helper<A>::cast(y)->print();
dynamic_cast_helper<A>::cast(yw)->print();
return 0;
}
(Demo)
Your cast dynamic_pointer_cast<typename T::element_type>(iteratableList[i]) then becomes dynamic_cast_helper<typename smart_pointer_info<T>::element_type>::cast(iteratableList[i]) and all the types along the way get inferred by the compiler.
Consider this working code. The function searchByDataMember uses a pointer to data member as argument to search a value among a container.
#include <iostream>
#include <list>
#include <string>
template <typename Container, typename T, typename DataPtr>
typename Container::value_type searchByDataMember (const Container& container, const T& t,
DataPtr ptr) {
for (const typename Container::value_type& x : container) {
if (x->*ptr == t)
return x;
}
return typename Container::value_type{};
}
struct Object {
int ID, value;
std::string name;
Object (int i, int v, const std::string& n) : ID(i), value(v), name(n) {}
};
std::list<Object*> objects { new Object(5,6,"Sam"), new Object(11,7,"Mark"),
new Object(9,12,"Rob"), new Object(2,11,"Tom"), new Object(15,16,"John") };
int main() {
const Object* object = searchByDataMember (objects, 11, &Object::value);
std::cout << object->name << '\n'; // Tom
}
So how to extend the above to using a variadic list of pointers to data members as arguments, in the event that a data member pointed to itself has data members to search? For example,
#include <iostream>
#include <list>
#include <string>
template <typename Container, typename T, typename... DataPtrs>
typename Container::value_type searchByDataMember (const Container& container, const T& t,
DataPtrs... ptrs) {
// What to put here???
}
struct Thing {
int ID, value;
std::string name;
Thing (int i, int v, const std::string& n) : ID(i), value(v), name(n) {}
};
struct Object {
int rank;
Thing* thing;
Object (int r, Thing* t) : rank(r), thing(t) {}
};
std::list<Object*> objects { new Object(8, new Thing(5,6,"Sam")), new Object(2, new Thing(11,7,"Mark")),
new Object(1, new Thing(9,12,"Rob")), new Object(9, new Thing(2,11,"Tom"))};
int main() {
// The desired syntax.
// const Object* object = searchByDataMember (objects, 11, &Object::thing, &Thing::value);
// std::cout << object->thing->name << '\n'; // Tom (the desired output)
}
So here we wish to search among the container objects for the Object* that has a Thing* data member whose value data member is 11, which is the Object* that has "Tom". There is to be no limit on how big a chain of pointers to data members can be passed into searchByDataMember.
You need a way to apply operator ->* in succession:
template <typename T, typename MPtr>
auto arrow(T* obj, MPtr mptr)
{
return obj->*mptr;
}
template <typename T, typename MPtr, typename ... MPtrs>
auto arrow(T* obj, MPtr mptr, MPtrs... mptrs)
{
return arrow(obj->*mptr, mptrs...);
}
Then your search function is simple, something like: (I prefer to return iterator over value btw)
template <typename Container, typename T, typename... DataPtrs>
auto searchByDataMember (const Container& container, const T& t, DataPtrs... ptrs)
{
return std::find_if(std::begin(container), std::end(container),
[&](const auto&e) {
return arrow(e, ptrs...) == t;
});
}
Demo
I am a newer for C++, and my first language is Chinese, so my words with English may be unmeaningful, say sorry first.
I know there is a way to write a function with variable parameters which number or type maybe different each calling, we can use the macros of va_list,va_start and va_end. But as everyone know, it is the C style. When we use the macros, we will lose the benefit of type-safe and auto-inference, then I try do it whit C++ template. My work is followed:
#include<iostream>
#include<vector>
#include<boost/any.hpp>
struct Argument
{
typedef boost::bad_any_cast bad_cast;
template<typename Type>
Argument& operator,(const Type& v)
{
boost::any a(v);
_args.push_back(a);
return *this;
}
size_t size() const
{
return _args.size();
}
template<typename Type>
Type value(size_t n) const
{
return boost::any_cast<Type>(_args[n]);
}
template<typename Type>
const Type* piont(size_t n) const
{
return boost::any_cast<Type>(&_args[n]);
}
private:
std::vector<boost::any> _args;
};
int sum(const Argument& arg)
{
int sum=0;
for(size_t s=0; s<arg.size(); ++s)
{
sum += arg.value<int>(s);
}
return sum;
}
int main()
{
std::cout << sum((Argument(), 1, 3, 4, 5)) << std::endl;
return 0;
}
I think it's ugly, I want to there is a way to do better? Thanks, and sorry for language errors.
You can do something like this:
template <typename T>
class sum{
T value;
public:
sum ()
: value() {};
// Add one argument
sum<T>& operator<<(T const& x)
{ value += x; return *this; }
// to get funal value
operator T()
{ return value;}
// need another type that's handled differently? Sure!
sum<T>& operator<<(double const& x)
{ value += 100*int(x); return *this; }
};
#include <iostream>
int main()
{
std::cout << (sum<int>() << 5 << 1 << 1.5 << 19) << "\n";
return 0;
}
Such technique (operator overloading and stream-like function class) may solve different problems with variable arguments, not only this one. For example:
create_window() << window::caption - "Hey" << window::width - 5;
// height of the window and its other parameters are not set here and use default values
After giving it some thought, I found a way to do it using a typelist. You don't need an any type that way, and your code becomes type-safe.
It's based on building a template structure containing a head (of a known type) and a tail, which is again a typelist. I added some syntactic sugar to make it more intuitive: use like this:
// the 1 argument processing function
template< typename TArg > void processArg( const TArg& arg ) {
std::cout << "processing " << arg.value << std::endl;
}
// recursive function: processes
// the first argument, and calls itself again for
// the rest of the typelist
// (note: can be generalized to take _any_ function
template< typename TArgs >
void process( const TArgs& args ) {
processArg( args.head );
return process( args.rest );
}
template<> void process<VoidArg>( const VoidArg& arg ){}
int main() {
const char* p = "another string";
process( (arglist= 1, 1.2, "a string", p ) );
}
And here is the argument passing framework:
#include <iostream>
// wrapper to abstract away the difference between pointer types and value types.
template< typename T > struct TCont {
T value;
TCont( const T& t ):value(t){}
};
template<typename T, size_t N> struct TCont< T[N] > {
const T* value;
TCont( const T* const t ) : value( t ) { }
};
template<typename T> struct TCont<T*> {
const T* value;
TCont( const T* t ): value(t){}
};
// forward definition of type argument list
template< typename aT, typename aRest >
struct TArgList ;
// this structure is the starting point
// of the type safe variadic argument list
struct VoidArg {
template< typename A >
struct Append {
typedef TArgList< A, VoidArg > result;
};
template< typename A >
typename Append<A>::result append( const A& a ) const {
Append<A>::result ret( a, *this );
return ret;
}
//syntactic sugar
template< typename A > typename Append<A>::result operator=( const A& a ) const { return append(a); }
} const arglist;
// typelist containing an argument
// and the rest of the arguments (again a typelist)
//
template< typename aT, typename aRest >
struct TArgList {
typedef aT T;
typedef aRest Rest;
typedef TArgList< aT, aRest > Self;
TArgList( const TCont<T>& head, const Rest& rest ): head( head ), rest( rest ){}
TCont<T> head;
Rest rest;
template< typename A > struct Append {
typedef TArgList< T, typename Rest::Append<A>::result > result;
};
template< typename A >
typename Append< A >::result append( const A& a ) const {
Append< A >::result ret ( head.value, (rest.append( a ) ) );
return ret;
}
template< typename A > typename Append<A>::result operator,( const A& a ) const { return append(a); }
};