I have the following template function:
struct ms {
template <typename... Args>
void update(string& query, Args&... args);
};
template <typename... Args>
void ms::update(string& query, Args&... args)
{
const int size = sizeof...(args);
vector<string> vec = { args... };
for (int i = 0; i < size; ++i) {
cout << query << ": " << vec[i] << endl;
}
}
However, I would like to eliminate the use of template and simply make this a member function that takes one or more string arguments. All of the documentation and examples on Variadic Functions I can find show use of character arrays and doing a while pointer != null to grab each value in the array.
If I do something like:
void update(string& query, string&... args);
how would I iterate over the args parameters, if there are any?
This is an array_view<T>:
template<class T>
struct array_view;
template<class D>
struct array_view_base;
template<class T>
struct array_view_base<array_view<T>> {
T* b=0; T* e=0;
T* begin() const { return b; }
T* end() const { return e; }
T& operator[](std::size_t i)const{ return begin()[i]; }
std::size_t size() const { return end()-begin(); }
T& front() const { return *begin(); }
T& back() const { return *(end()-1); }
array_view<T> without_front( std::size_t N=1 ) const {
N=(std::min)(N, size());
return {begin()+N, end()};
}
array_view<T> without_back( std::size_t N=1 ) const {
N=(std::min)(N, size());
return {begin(), end()-N};
}
array_view_base( T* s, T* f ):b(s),e(f){}
array_view_base( T* s, std::size_t sz ):array_view_base(s, s+sz) {}
template<std::size_t N>
array_view_base( T(&arr)[N] ):array_view_base(arr, N) {}
template<class C,
std::enable_if_t<!std::is_same<std::decay_t<C>, array_view<T>>{}, int> =0
>
array_view_base( C&& c ):array_view_base(c.data(), c.size()) {}
};
template<class T>
struct array_view:array_view_base<array_view<T>> {
using array_view_base<array_view<T>>::array_view_base;
};
template<class T>
struct array_view<T const>:array_view_base<array_view<T const>> {
using array_view_base<array_view<T const>>::array_view_base;
array_view( std::initializer_list<T> il ):array_view( std::addressof(*il.begin()), il.size() ) {}
};
it works a bit like a gsl::span<T>. it is a contiguous range of T.
Unlike gsl::span<T>, array_view<T const> can be constructed from an initializer_list<T>.
With it your code should look like:
struct ms {
void update(string const& query, array_view<string const> args);
};
void ms::update(string const& query, array_view<string const> args)
{
for (int i = 0; i < args.size(); ++i) {
cout << query << ": " << args[i] << endl;
}
}
and you call it like:
ms{}.update( "Hello", {"one", "two", "three"} );
Live example.
As a worse example, simply take a std::vector<std::string>.
At point of call
ms{}.update( "Hello", {"one", "two", "three"} );
also works. Unlike my solution, this causes a memory allocation.
Related
How do you avoid code duplication when using varadic parameters in c++? Notice that I'm using templates recursively to achieve my goals, therefore I need some base cases and a recursive case. This creates a lot of code duplication, are there ways I could reduce this duplication?
Below, an example is provided of code that creates an arbitrary tensor (N dimensional array).
It's working fine but there's too much duplication. How can I avoid writing duplicated code when using template parameter packs recursively like this?
#include <cstddef>
#include <array>
#include <iostream>
template<typename T, std::size_t...>
class Tensor;
template<typename T, std::size_t N>
class Tensor<T, N> {
using Type = std::array<T, N>;
Type data;
public:
Tensor()
{
zero();
}
void zero()
{
fill(0);
}
Type::iterator begin() { return data.begin(); }
Type::iterator end() { return data.end(); }
void fill(T value)
{
std::fill(data.begin(), data.end(), value);
}
void print() const
{
std::cout << "[";
for(const auto& v : data)
{
std::cout << v << ",";
}
std::cout << "]";
}
};
template<typename T, std::size_t N, std::size_t M>
class Tensor<T, N, M>
{
using Type = std::array<Tensor<T, M>, N>;
Type data;
public:
Tensor()
{
zero();
}
void zero()
{
fill(0);
}
Type::iterator begin() { return data.begin(); }
Type::iterator end() { return data.end(); }
void fill(T value)
{
for(auto& v: data) {
std::fill(v.begin(), v.end(), value);
}
}
void print() const
{
std::cout << "[";
for(const auto& v : data)
{
v.print();
std::cout << ",";
}
std::cout << "]";
}
};
template<typename T, std::size_t N, std::size_t... M>
class Tensor<T, N, M...>
{
using Type = std::array<Tensor<T, M...>, N>;
Type data;
public:
Type::iterator begin() { return data.begin(); }
Type::iterator end() { return data.end(); }
Tensor()
{
zero();
}
void zero()
{
fill(0);
}
void fill(T value)
{
for(auto& v: data) {
v.fill(value);
}
}
void print() const
{
std::cout << "[";
for(const auto& v : data)
{
v.print();
std::cout << ",";
}
std::cout << "]";
}
};
The only difference between a single-dimension tensor and a multiple-dimension tensor is the type of std::array, T for single and Tensor<T, M...> for another.
template<typename T, std::size_t N, std::size_t... M>
class Tensor<T, N, M...> {
using InnerT = std::conditional_t<(sizeof...(M) > 0),
Tensor<T, M...>,
T>;
using Type = std::array<InnerT, N>;
Type data;
}
Then, use if constexpr to distinguish single-dimension case,
void fill(T value)
{
if constexpr(sizeof...(M) > 0) {
for(auto& v: data) {
v.fill(value);
}
} else {
std::fill(data.begin(), data.end(), value);
}
}
void print() const
{
std::cout << "[";
for(const auto& v : data)
{
if constexpr(sizeof...(M) > 0) {
v.print();
std::cout << ",";
} else {
std::cout << v << ",";
}
}
std::cout << "]";
}
Demo
How to handle an api which returns different data types for the same input data types?
Looking at the below example, apicall should return a date or a string depending on the input attribute:
#include <iostream>
#include <string>
using namespace std;
???? apicall(string datatype, string attribute)
{
// code
}
int main(int argc, char** argv)
{
string datatype = "Thomas"
string attribute = "bithday"
cout << apicall(datatype, attribute) << endl;
string datatype = "Thomas"
string attribute = "address"
cout << apicall(datatype, attribute) << endl;
}
What could be in place of ???? (apicall return datatype) and how to handle these cases?
I am trying to understand these concepts as my experience to date has been with duck typed scripting languages.
The ideal solution is to use a std::variant, which is a safe union type like.
This allows you to write the following:
using DateOrString = std::variant<DateType, std::string>;
DateOrString api_call(std::string, std::string) {
// you can return both DateType and std::string
}
// ...
auto result = api_call("", "");
auto& str = std::get<std::string>(result);
Unfortunately std::variant is a C++17 feature. However different compilers already support it.
As already has been suggested, boost has a variant class and you can use it with any C++ standard.
As last option, you may implement a "variant-like" class which handles both a date and a string. Your function should return it.
Here a demo how to quickly implement that kind of class.
Note that that class is safe because the type is checked at runtime.
As a variant object, your callee function should branch on the type, something like:
auto result = api_call(/*...*/);
if (result.is_string()) {
// result is a string
const auto& str = result.get_string();
} else {
// result is a date
const auto& date = result.get_date();
}
... returns different data types for the same input data types?
This is literally impossible. A function is defined with one (or zero) return types, and zero or more input parameter types.
The workarounds are:
Write a single function returning a variant type, such as std::variant in C++17, or Boost.Variant if that's not available.
Write multiple functions with different return types (the caller just has to choose the right one)
Invert control, so that instead of returning a value, you pass an object capable of processing all the required types:
struct APIHandler {
virtual ~APIHandler() {}
virtual void operator()(int) {}
virtual void operator()(string) {}
};
void apicall(string name, string attr, APIHandler &h) {
// dummy implementation
if (attr == "address") {
h("123 Woodford Road");
} else if (attr == "birthday") {
h(19830214);
}
}
// implement your type-specific logic here
struct MyHandler: APIHandler {
void operator()(int i) override {
cout << "got an int:" << i << '\n';
}
void operator()(string s) override {
cout << "got a string:" << s << '\n';
}
};
// and use it like:
MyHandler mh;
apicall("Thomas", "birthday", mh);
apicall("Thomas", "address", mh);
You want a std::variant in C++17 or a boost::variant or roll your own crude variant something like this:
constexpr std::size_t max() { return 0; }
template<class...Ts>
constexpr std::size_t max( std::size_t t0, Ts...ts ) {
return (t0<max(ts...))?max(ts...):t0;
}
template<class T0, class...Ts>
struct index_of_in;
template<class T0, class...Ts>
struct index_of_in<T0, T0, Ts...>:std::integral_constant<std::size_t, 0> {};
template<class T0, class T1, class...Ts>
struct index_of_in<T0, T1, Ts...>:
std::integral_constant<std::size_t,
index_of_in<T0, Ts...>::value+1
>
{};
struct variant_vtable {
void(*dtor)(void*) = 0;
void(*copy)(void*, void const*) = 0;
void(*move)(void*, void*) = 0;
};
template<class T>
void populate_vtable( variant_vtable* vtable ) {
vtable->dtor = [](void* ptr){ static_cast<T*>(ptr)->~T(); };
vtable->copy = [](void* dest, void const* src){
::new(dest) T(*static_cast<T const*>(src));
};
vtable->move = [](void* dest, void* src){
::new(dest) T(std::move(*static_cast<T*>(src)));
};
}
template<class T>
variant_vtable make_vtable() {
variant_vtable r;
populate_vtable<T>(&r);
return r;
}
template<class T>
variant_vtable const* get_vtable() {
static const variant_vtable table = make_vtable<T>();
return &table;
}
template<class T0, class...Ts>
struct my_variant {
std::size_t index = -1;
variant_vtable const* vtable = 0;
static constexpr auto data_size = max(sizeof(T0),sizeof(Ts)...);
static constexpr auto data_align = max(alignof(T0),alignof(Ts)...);
template<class T>
static constexpr std::size_t index_of() {
return index_of_in<T, T0, Ts...>::value;
}
typename std::aligned_storage< data_size, data_align >::type data;
template<class T>
T* get() {
if (index_of<T>() == index)
return static_cast<T*>((void*)&data);
else
return nullptr;
}
template<class T>
T const* get() const {
return const_cast<my_variant*>(this)->get<T>();
}
template<class F, class R>
using applicator = R(*)(F&&, my_variant*);
template<class T, class F, class R>
static applicator<F, R> get_applicator() {
return [](F&& f, my_variant* ptr)->R {
return std::forward<F>(f)( *ptr->get<T>() );
};
}
template<class F, class R=typename std::result_of<F(T0&)>::type>
R visit( F&& f ) & {
if (index == (std::size_t)-1) throw std::invalid_argument("variant");
static const applicator<F, R> table[] = {
get_applicator<T0, F, R>(),
get_applicator<Ts, F, R>()...
};
return table[index]( std::forward<F>(f), this );
}
template<class F,
class R=typename std::result_of<F(T0 const&)>::type
>
R visit( F&& f ) const& {
return const_cast<my_variant*>(this)->visit(
[&f](auto const& v)->R
{
return std::forward<F>(f)(v);
}
);
}
template<class F,
class R=typename std::result_of<F(T0&&)>::type
>
R visit( F&& f ) && {
return visit( [&f](auto& v)->R {
return std::forward<F>(f)(std::move(v));
} );
}
explicit operator bool() const { return vtable; }
template<class T, class...Args>
void emplace( Args&&...args ) {
clear();
::new( (void*)&data ) T(std::forward<Args>(args)...);
index = index_of<T>();
vtable = get_vtable<T>();
}
void clear() {
if (!vtable) return;
vtable->dtor( &data );
index = -1;
vtable = nullptr;
}
~my_variant() { clear(); }
my_variant() {}
void copy_from( my_variant const& o ) {
if (this == &o) return;
clear();
if (!o.vtable) return;
o.vtable->copy( &data, &o.data );
vtable = o.vtable;
index = o.index;
}
void move_from( my_variant&& o ) {
if (this == &o) return;
clear();
if (!o.vtable) return;
o.vtable->move( &data, &o.data );
vtable = o.vtable;
index = o.index;
}
my_variant( my_variant const& o ) {
copy_from(o);
}
my_variant( my_variant && o ) {
move_from(std::move(o));
}
my_variant& operator=(my_variant const& o) {
copy_from(o);
return *this;
}
my_variant& operator=(my_variant&& o) {
move_from(std::move(o));
return *this;
}
template<class T,
typename std::enable_if<!std::is_same<typename std::decay<T>::type, my_variant>{}, int>::type =0
>
my_variant( T&& t ) {
emplace<typename std::decay<T>::type>(std::forward<T>(t));
}
};
then your code looks like:
variant<string, int> apicall(string datatype, string attribute)
{
if (datatype > attribute) return string("hello world");
return 7;
}
int main()
{
string datatype = "Thomas"
string attribute = "bithday"
apicall(datatype, attribute).visit([](auto&&r){
cout << r << endl;
});
string datatype = "Thomas"
string attribute = "address"
apicall(datatype, attribute).visit([](auto&& r){
cout << r << endl;
});
}
with whatever visit or apply_visitor free function or method your particular variant supports.
This gets much more annoying in C++11 as we don't have generic lambdas.
You could use a variant, but it's up to the caller site to check the results. Boost and std defines two variant types, i.e. std::variant and std::any.
So, I have a class, which has an array of arrays as a private member. I wish to have two constructors for each case (1D or 2D). But of course their declaration happens to be the same, so template deduction can't do its job without me doing something about it. Here's the code:
Edit: I also need it to work with STL containers like vector or C++ array. That is why I am overcomplicating and not going with the "arrays" fix.
#include <iostream>
#include <array>
template<class T, std::size_t rows_t, std::size_t cols_t>
class test
{
private:
std::array<std::array<T, cols_t>, rows_t> _data;
public:
auto begin() { return this->_data.begin(); }
auto end() { return this->_data.end(); }
//CONSTRUCTOR
template<class type_t>
test(const type_t &arr)
{
std::size_t j = 0;
for (const auto &num : arr)
this->_data[0][j++] = num;
}
template<class type_t>
test(const type_t &arr)
{
std::size_t i = 0;
for (const auto &el : arr)
{
std::size_t j = 0;
for (const auto &num : el)
this->_data[i][j++] = num;
++i;
}
}
};
int main()
{
double arr[3] = { 1, 2, 3 };
double arr2[2][2] = { {1, 2}, {3, 4} };
test<double, 1, 3> obj = arr;
test<double, 2, 2> obj2 = arr2;
for (const auto &i : obj2)
{
for (const auto &j : i)
std::cout << j << " ";
std::cout << std::endl;
}
std::cin.get();
}
Note: I've been reading about enable_if, but I don't quite understand how it works. Can it be done with that?
The constructors should not be the same, but you have only provided the most generic matching possible.
SFINAE is not necessary here. Just provide a constructor for a 1D array, and a separate constructor for a 2D array:
template <typename T2, std::size_t N>
test( const T2 (&a)[N] )
{
...
}
template <typename T2, std::size_t M, std::size_t N>
test( const T2 (&a)[M][N] )
{
...
}
Another note: POSIX reserves typenames ending with "_t", so it is typically a good idea to avoid them in your own code. (Obnoxious, I know.) Standard C++ will use Camel Case of the form: RowsType, etc, and then typedef a rows_type for users of the class.
Notice, however, that rows_t is not actually a type -- it is a value. A better name would be something like NRows.
Hope this helps.
First, you have to "teach" the compiler what's 2D and what's not. Hence, you have to define something like the following type trait:
template<typename T>
struct is2D : public std::false_type {};
template<typename T, std::size_t N, std::size_t M>
struct is2D<std::array<std::array<T, M>, N>> : std::true_type {};
template<typename T>
struct is2D<std::vector<std::vector<T>>> : std::true_type {};
template<typename T, std::size_t N, std::size_t M>
struct is2D<T[N][M]> : std::true_type {};
Then you could set up your class definition in the following way:
template<class T, std::size_t rows_t, std::size_t cols_t>
class test{
std::array<std::array<T, cols_t>, rows_t> _data;
template<class type_t>
std::enable_if_t<!is2D<type_t>::value, void>
test_init(type_t const &arr) {
std::size_t j = 0;
for (const auto &num : arr) _data[0][j++] = num;
}
template<class type_t>
std::enable_if_t<is2D<type_t>::value, void>
test_init(type_t const &arr) {
std::size_t i = 0;
for(const auto &el : arr) {
std::size_t j = 0;
for (const auto &num : el) _data[i][j++] = num;
++i;
}
}
public:
auto &operator[](const std::size_t &i) { return this->_data[i]; }
auto begin() { return this->_data.begin(); }
auto end() { return this->_data.end(); }
//CONSTRUCTOR
template<class type_t> test(type_t const &arr) { test_init(arr); }
};
LIVE DEMO
When you defined a template in C++ (with type parameter) you can pass as type actually a pointer to a type, for example:
MyClass<Foo*>... // edited
I wonder if this is really used? Because such question is too broad, let's focus on core C++, meaning -- is it used in STL or Boost? If yes (in STL/Boost) for what purpose?
Please note that I am asking about passing pointer as an argument (from OUTSIDE). Using pointer to passed argument INSIDE template is another story, and I don't ask about that.
Update
The difference between passing a pointer and using a pointer.
Take a look at those posts:
How is vector implemented in C++ and
passing pointer type to template argument .
myname<char*>(...
myname is a function. The type which is passed to template (template, not a function) is pointer to char.
Now, using a pointer inside:
template <class T...
class vector {
private:
T* data_;
You pass an int (for example) but nothing stops the template of using a pointer to it.
I am interested in the first, not the second, case.
A pointer is just a type, so anywhere you can use some_template<T> you might also want to use some_template<T*>
The parts of the standard library based in the STL use std::iterator_traits<Iter> in many places, and Iter might be a pointer type.
Some implementations of std::unique_ptr<T, D> use a data member of type std::tuple<T*, D> (e.g. this is true for GCC's implementation, but this is an implementation detail that you should not care about).
Like I commented, I remembered a use case where functions would be registered as event callbacks by their actual address.
This way the trampoline functions to invoke member functions would be generated statically for each member function registered.
I'm not sure this is something I'd actually have a use for, but it does demonstrate a (contrived?) way in which pointer-to-function template arguments can be used.
#include <iostream>
#include <vector>
const static auto null = nullptr;
template<typename TFuncSignature>
class Callback;
template<typename R, typename A1>
class Callback<R (A1)> {
public:
typedef R (*TFunc)(void*, A1);
Callback() : obj(0), func(0) {}
Callback(void* o, TFunc f) : obj(o), func(f) {}
R operator()(A1 a1) const {
return (*func)(obj, a1);
}
typedef void* Callback::*SafeBoolType;
operator SafeBoolType () const {
return func != 0? &Callback::obj : 0;
}
bool operator! () const {
return func == 0;
}
bool operator== ( const Callback<R (A1)>& right ) const {
return obj == right.obj && func == right.func;
}
bool operator!= ( const Callback<R (A1)>& right ) const {
return obj != right.obj || func != right.func;
}
private:
void* obj;
TFunc func;
};
template<typename R, class T, typename A1>
struct DeduceMemCallbackTag {
template<R (T::*Func)(A1)>
static R Wrapper(void* o, A1 a1) {
return (static_cast<T*>(o)->*Func)(a1);
}
template<R (T::*Func)(A1)>
inline static Callback<R (A1)> Bind(T* o) {
return Callback<R (A1)>(o, &DeduceMemCallbackTag::Wrapper<Func>);
}
};
template<typename R, typename A1>
struct DeduceStaticCallbackTag {
template<R (*Func)(A1)>
static R Wrapper(void*, A1 a1) {
return (*Func)(a1);
}
template<R (*Func)(A1)>
inline static Callback<R (A1)> Bind( ) {
return Callback<R (A1)>( 0, &DeduceStaticCallbackTag::Wrapper<Func> );
}
};
template<typename R, class T, typename A1>
DeduceMemCallbackTag<R, T, A1> DeduceMemCallback(R (T::*)(A1)) {
return DeduceMemCallbackTag<R, T, A1>();
}
template<typename R, typename A1>
DeduceStaticCallbackTag<R, A1> DeduceStaticCallback(R (*)(A1)) {
return DeduceStaticCallbackTag<R, A1>();
}
template <typename T1> class Event {
public:
typedef void(* TSignature)( T1 );
typedef Callback<void( T1 )> TCallback;
protected:
std::vector<TCallback> invocations;
std::vector<Event<T1>*> events;
public:
const static int ExpectedFunctorCount = 2;
Event () : invocations(), events() {
invocations.reserve( ExpectedFunctorCount );
events.reserve( ExpectedFunctorCount );
}
template <void (* TFunc)(T1)> void Add ( ) {
TCallback c = DeduceStaticCallback( TFunc ).template Bind< TFunc >( );
invocations.push_back( c );
}
template <typename T, void (T::* TFunc)(T1)> void Add ( T& object ) {
Add<T, TFunc>( &object );
}
template <typename T, void (T::* TFunc)(T1)> void Add ( T* object ) {
TCallback c = DeduceMemCallback( TFunc ).template Bind< TFunc >( object );
invocations.push_back( c );
}
void Invoke ( T1 t1 ) {
size_t i;
for ( i = 0; i < invocations.size(); ++i ) {
invocations[i]( t1 );
}
for ( i = 0; i < events.size(); ++i ) {
(*events[i])( t1 );
}
}
void operator() ( T1 t1 ) {
Invoke( t1 );
}
size_t InvocationCount ( ) {
return events.size( ) + invocations.size( );
}
template <void (* TFunc)(T1)> bool Remove ( ) {
TCallback target = DeduceStaticCallback( TFunc ).template Bind< TFunc >( );
for ( size_t i = 0; i < invocations.size(); ++i ) {
TCallback& inv = invocations[i];
if ( target == inv ) {
invocations.erase( invocations.begin() + i );
return true;
}
}
return false;
}
template <typename T, void (T::* TFunc)(T1)> bool Remove ( T& object ) {
return Remove<T, TFunc>( &object );
}
template <typename T, void (T::* TFunc)(T1)> bool Remove ( T* object ) {
TCallback target = DeduceMemCallback( TFunc ).template Bind< TFunc >( object );
for ( size_t i = 0; i < invocations.size(); ++i ) {
TCallback& inv = invocations[i];
if ( target == inv ) {
invocations.erase( invocations.begin() + i );
return true;
}
}
return false;
}
};
namespace IntStatic {
void VoidTest () { std::cout << "INTO THE VOID" << std::endl; }
void IntTest (int num) { std::cout << "Got myself a " << num << " !" << std::endl; }
void IntTest2 (int num) { std::cout << "Now _I_ Got myself a " << num << " !" << std::endl; }
}
struct Int {
void Test (int num) { std::cout << num << " on the inside of a class... ?" << std::endl; }
void Test2 (int num) { std::cout << num << " on the inside of a struct, yo !" << std::endl; }
static void Test3(int snum) { std::cout << snum << " on the inside of a class... ?" << std::endl; }
};
int main(int argc, char* argv[]) {
Event<int> intev;
Int i;
intev.Add<Int, &Int::Test>(i);
intev.Add<&IntStatic::IntTest>();
intev.Add<&IntStatic::IntTest2>();
//intev.Add( Int::Test3 );
intev(20);
intev.Remove<&IntStatic::IntTest>();
intev.Remove<&IntStatic::IntTest>();
intev.Remove<Int, &Int::Test>(i);
//intev.Remove( Int::Test3 );
//intev.Remove( i, &Int::Test );
intev(20);
return 0;
}
The actual code is famously written by #ThePhD, credits to him. See it Live on Coliru
You could have different semantics by specialization, e.g.:
template <typename T> struct referred_sizeof {
static constexpr size_t value = sizeof(T);
};
Now, this could be specialized:
template <typename T> struct referred_sizeof<T*> {
static constexpr size_t value = sizeof(T);
};
template <typename T> struct referred_sizeof <boost::optional<T*> > {
static constexpr size_t value = sizeof(boost::optional<T*>) + sizeof(T);
};
Which makes the behaviour:
static_assert(referred_sizeof <int>::value == referred_sizeof <int*>::value, "passes");
This application is what others referred to in comments as implementing traits classes.
Full sample, adding specialization for boost::tuple<...> just for fun: See it Live On Coliru
int main()
{
report<double>();
report<double *>();
report<boost::optional<double> >();
report<boost::optional<double> *>();
report<boost::optional<double *> *>();
report<boost::tuple<boost::optional<double *> *, double> >();
}
Prints
void report() [with T = double]: referred_sizeof is 8
void report() [with T = double*]: referred_sizeof is 8
void report() [with T = boost::optional<double>]: referred_sizeof is 16
void report() [with T = boost::optional<double>*]: referred_sizeof is 16
void report() [with T = boost::optional<double*>*]: referred_sizeof is 24
void report() [with T = boost::tuples::tuple<boost::optional<double*>*, double>]: referred_sizeof is 40
Full implementation for reference
#include <iostream>
#include <boost/optional.hpp>
#include <boost/tuple/tuple.hpp>
template <typename... Ts> struct referred_sizeof;
// base cases
template <typename T> struct referred_sizeof<T> {
static constexpr size_t value = sizeof(T);
};
template <typename T> struct referred_sizeof<T*> {
static constexpr size_t value = referred_sizeof<T>::value;
};
template <typename T> struct referred_sizeof<boost::optional<T*> > {
static constexpr size_t value = sizeof(boost::optional<T*>) + referred_sizeof<T>::value;
};
template <typename... Ts> struct referred_sizeof<boost::tuple<Ts...> > {
static constexpr size_t value = referred_sizeof<Ts...>::value; // TODO take into account padding/alignment overhead?
};
static_assert(referred_sizeof<int>::value == referred_sizeof<int*>::value, "passes");
template <typename T1, typename... Ts> struct referred_sizeof<T1, Ts...> {
static constexpr size_t value = referred_sizeof<T1>::value + referred_sizeof<Ts...>::value;
};
template <typename T>
void report()
{
std::cout << __PRETTY_FUNCTION__ << ": referred_sizeof is " << referred_sizeof<T>::value << "\n";
}
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); }
};