I'm trying to create a variadic function that takes any amount of arguments, but I'd like to specialize the case where only two arguments with iterators are passed in. Passing in two arguments of non-iterators, should still use the generic variadic version. I'm hitting a static_assert failure that I haven't been able to overcome. It seems like it's trying to evaluate the entire expression in with_iterator_args, which fails if the function has less than two arguments, rather than skipping the evaluation of the remainder when the check for 2 arguments already yielded false.
Is there a way to do this without adding two more overloads for the one and two argument case?
This is what I have so far:
#include <iostream>
#include <vector>
#include <tuple>
// inspired by https://stackoverflow.com/a/7943765/2129246
template <typename... Args>
struct args_traits
{
enum { arity = sizeof...(Args) };
template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
};
};
// based on: https://stackoverflow.com/a/30766365/2129246
template <typename T>
struct is_iterator
{
static char test(...);
template <typename U,
typename=typename std::iterator_traits<U>::difference_type,
typename=typename std::iterator_traits<U>::pointer,
typename=typename std::iterator_traits<U>::reference,
typename=typename std::iterator_traits<U>::value_type,
typename=typename std::iterator_traits<U>::iterator_category
> static long test(U&&);
constexpr static bool value = std::is_same<decltype(test(std::declval<T>())),long>::value;
};
template<typename Arg1, typename Arg2>
struct is_iterator_args
{
constexpr static bool value = is_iterator<Arg1>::value && is_iterator<Arg2>::value;
};
template<typename... Args>
struct with_iterator_args
{
constexpr static bool value = args_traits<Args...>::arity == 2
&& is_iterator_args<typename args_traits<Args...>::template arg<0>::type, typename args_traits<Args...>::template arg<1>::type>::value;
};
template <typename T, typename... Args,
typename = typename std::enable_if<!with_iterator_args<Args...>::value>::type>
void some_func(T first, Args&&... args)
{
std::cout << "func(" << first << ") called with " << sizeof...(args) << " args" << std::endl;
}
template <typename T, typename Begin, typename End,
typename = typename std::enable_if<is_iterator_args<Begin, End>::value>::type>
void some_func(T first, Begin begin, End end)
{
std::cout << "func(" << first << ") called with iterators: " << std::distance(begin, end) << std::endl;
}
int main()
{
std::vector<int> v{1, 2, 3};
some_func(1, v.begin(), v.end()); // special case, using iterators
some_func(1, "arg2", 3, std::string("arg4"));
some_func(1, "arg2");
some_func(1);
some_func(1, "arg2", 3, std::string("arg4"), 5.67);
return 0;
}
This is what fails:
In file included from test.cpp:3:
/usr/include/c++/9/tuple: In instantiation of ‘struct std::tuple_element<0, std::tuple<> >’:
/usr/include/c++/9/tuple:1285:12: required from ‘struct std::tuple_element<1, std::tuple<const char (&)[5]> >’
test.cpp:14:69: required from ‘struct args_traits<const char (&)[5]>::arg<1>’
test.cpp:45:3: required from ‘constexpr const bool with_iterator_args<const char (&)[5]>::value’
test.cpp:49:37: required by substitution of ‘template<class T, class ... Args, class> void some_func(T, Args&& ...) [with T = int; Args = {const char (&)[5]}; <template-parameter-1-3> = <missing>]’
test.cpp:68:21: required from here
/usr/include/c++/9/tuple:1303:25: error: static assertion failed: tuple index is in range
1303 | static_assert(__i < tuple_size<tuple<>>::value,
| ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
The problem in your case is that both expression before and after && must compile - even if the expression after the && will not be used.
My first try was to leverage C++17 constexpr if.
template<typename... Args>
struct with_iterator_args
{
private:
constexpr static bool value_checker() {
if constexpr (args_traits<Args...>::arity == 2) {
return is_iterator_args<typename args_traits<Args...>::template arg<0>::type, typename args_traits<Args...>::template arg<1>::type>::value;
}
else {
return false;
}
}
public:
constexpr static bool value = value_checker();
};
If you need to stick with C++11, you could use std::conditional. Please note that I also use std::false_type and std::true_type.
template<typename... Args>
struct is_iterator_args :
std::conditional<is_iterator<typename args_traits<Args...>::template arg<0>::type>::value &&
is_iterator<typename args_traits<Args...>::template arg<1>::type>::value,
std::true_type, std::false_type>::type
{
};
template<typename... Args>
struct with_iterator_args :
std::conditional<sizeof...(Args) == 2, is_iterator_args<Args...>, std::false_type>::type
{
};
I seem to be able to only make it work by adding more overloads, and not using with_iterator_args:
template <typename T, typename... Args>
void some_func_common(T first, Args&&... args)
{
std::cout << "func(" << first << ") called with " << sizeof...(args) << " args" << std::endl;
}
template <typename T, typename A>
void some_func(T first, A arg)
{
some_func_common(first, arg);
}
template <typename T>
void some_func(T first)
{
some_func_common(first);
}
template <typename T, typename A1, typename A2, typename... Args,
typename = typename std::enable_if<!is_iterator_args<A1, A2>::value>::type>
void some_func(T first, A1 begin, A2 end, Args&&... args)
{
some_func_common(first, std::forward<A1>(begin), std::forward<A2>(end), std::forward<Args>(args)...);
}
template <typename T, typename Begin, typename End,
typename = typename std::enable_if<is_iterator_args<Begin, End>::value>::type>
void some_func(T first, Begin begin, End end)
{
std::cout << "func(" << first << ") called iterators: " << std::distance(begin, end) << std::endl;
}
It seems unnecessarily messy, though.
I am trying to add items to a map that is a private variable in a class based on if certain parameters are met. When I try to use the insert function for std::map or the [] operator, nothing happens. I don't even get an error. During debugging the code executes like everything is fine but the map stays empty.
I have tried multiple ways to insert to the map including the [] operator and different insert arguments.
class foo {
private:
std::map<std::string, int> map;
public:
void bar();
};
In cpp file:
void foo::bar() {
if(condition)
map.insert(std::make_pair("string", 1));
}
There are no error messages or warnings in the compiler or during debugging.
If the basic STD map usage works, maybe the problem sits in your condition implementation
#include <iostream>
#include <map>
class Foo {
std::map<std::string, int> map;
public:
void bar();
void print();
};
void Foo::bar() {
// if (condition) // weird condition causing failure
map.insert(std::make_pair("string", 1));
}
void Foo::print() {
std::cout << map.size() << std::endl;
std::cout << map.at("string") << std::endl;
}
int main(void) {
Foo foo;
foo.bar();
foo.print();
}
I have tried code written on some link provided for dynamic function call , but unable to run code on machine .I tried to run code present at stackoverflow.com/questions/15764078/dynamically-creating-a-c-function-argument-list-at-runtime through member function.
It is is giving bad call exception while running :
Code snnippets
#include <iostream>
#include <functional>
#include <stdexcept>
#include <string>
#include <boost/any.hpp>
class Test;
class Test
{
public:
template <typename Ret, typename... Args>
Ret callfunc (std::function<Ret(Args...)> func, std::vector<boost::any> anyargs);
template <typename Ret>
Ret callfunc (std::function<Ret()> func, std::vector<boost::any> anyargs)
{
if (anyargs.size() > 0)
throw std::runtime_error("oops, argument list too long");
return func();
}
template <typename Ret, typename Arg0, typename... Args>
Ret callfunc (std::function<Ret(Arg0, Args...)> func, std::vector<boost::any>anyargs){
if (anyargs.size() == 0)
throw std::runtime_error("oops, argument list too short");
Arg0 arg0 = boost::any_cast<Arg0>(anyargs[0]);
anyargs.erase(anyargs.begin());
std::function<Ret(Args... args)> lambda =
([=](Args... args) -> Ret {
return func(arg0, args...);
});
return callfunc (lambda, anyargs);
}
template <typename Ret, typename... Args>
std::function<boost::any(std::vector<boost::any>)> adaptfunc (Ret (Test::*func)(Args...)) {
std::function<Ret(Test*,Args...)> stdfunc = func;
std::function<boost::any(std::vector<boost::any>)> result =
([=](std::vector<boost::any> anyargs) -> boost::any {
return boost::any(callfunc(stdfunc, anyargs));
});
return result;
}
int func1 (int a)
{
std::cout << "func1(" << a << ") = ";
return 33;
}
};
int main ()
{
Test a;
std::vector<std::function<boost::any(std::vector<boost::any>)>> fcs =
{
a.adaptfunc(&Test::func1)};
std::vector<std::vector<boost::any>> args =
{{777}};
// correct calls will succeed
for (int i = 0; i < fcs.size(); ++i)
std::cout << boost::any_cast<int>(fcs[i](args[i])) << std::endl;
return 0;
}
This code compiled successfully
But it failed to run and crashed
In main function for loop.
Function needs typecast according to their signature e.g.:
a.adaptfunc((int(*)(int))&Test::func1)};
After this typecast function call will not fail
#ifndef LIST
#define LIST
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
typedef string ElementType;
class List
{
private:
class Node
{
public:
ElementType data;
Node * next;
Node()
:data(ElementType()), next(NULL)
{}
Node(ElementType initData)
:data(initData), next(NULL)
{}
}; // end of Node class
typedef Node * NodePointer;
public:
private:
NodePointer first;
int mySize;
}; // end of List class
This is my overloaded assignment operator, im not sure what im doing wrong, but im beyond frustrated,I've searched the web and read different forums and cannot find a solution to do this, could someone please push me in the right direction, I left out all my public member functions, because none of them operate on my overloaded assignment and copy constructor, I just wanted you guys to get an idea of my layout
overloaded assignment operator
List & List::operator=(const List &rightSide)
{
if(this != &rightSide)
{
this->~List();
}
NodePointer ptr = rightSide.first;
NodePointer cptr = ptr;
while(ptr != NULL)
{
cptr->next = new Node(ptr->data);
cptr = cptr->next;
ptr = ptr->next;
}
return *this;
}
this is my copy constructor
List::List(const List &source)
{
NodePointer ptr = source.first;
NodePointer cptr;
if(ptr == NULL)
{
cerr << "Bad allocation, empty list" << endl;
exit(1);
}
while(ptr != NULL)
{
cptr = new Node(ptr->data);
cptr->next = ptr->next;
ptr = ptr->next;
}
}
I have two constructors in C++, and one constructor calls the other one in order not to duplicate initialization logic.
#include <iostream>
#include <memory>
using namespace std;
class A
{
int x;
int y;
public:
A(int x)
{
cout << this << endl;
this->x = x;
}
A()
{
cout << this << endl;
A(20);
}
...
};
What is interesting is A() calls A(int), but the this pointer points to different address. Why is this? Or is this g++ bug?
int main(int argc, char *argv[]) {
A* a = new A();
}
0x7fa8dbc009d0 <-- from A()
0x7fff67d660d0 <-- from A(int)
I believe A(20); is constructing a different instance of A within that constructor, not invoking the other constructor on the same instance.
See Can I call a constructor from another constructor (do constructor chaining) in C++? for how to invoke another constructor from a constructor.
If you are using a compiler that supports C++11, I think you can achieve what you want with this definition of the A() constructor:
A(): A(20)
{
cout << this << endl;
}
A(20); is a statement which constructs a new instance of A, not a call to A's constructor on this.
You can't call another constructor overload inside a given constructor in C++03. However, you can achieve the same effect by using placement new. Replace:
A(20);
in your code with:
new (this) A(20);