template used in class involving a generic comparison function - c++

I am trying to write a Bheap in templates and the insert function involving a generic comparison function. What is the usual way to do this? I know how to use function pointer in C. But Is there any typical C++ way to do that?
Someone told the first one, which class F could represent anything any function. But I want this function to be a comparison function which like f(T,T). While the second guy say something about the functors
template <class T, class F>class Bheap
{
public:
Bheap<T>(int allocateSize);
void insert(T value, F f);
void getMax();
private:
int sizeUsed;
int allocateSize;
vector<T> myBheap;
};

You should implement your class and the insert function, assuming whatever is passed is correct in terms of the number of arguments. If the number of arguments is not 2, the compiler will let you know.
This is the desired effect in these cases. Let the compiler detect that the function is not valid, and thus the user has to change the function to the correct requirements.
A demonstration using your code would be this:
#include <vector>
template <class T, class F>
class Bheap
{
public:
Bheap(int allocateSize) {}
void insert(T value, F f)
{
f(value, value);
}
void getMax();
private:
int sizeUsed;
int allocateSize;
std::vector<T> myBheap;
};
void SomeFunction(int, int)
{}
int main()
{
typedef void (*fn)(int, int);
Bheap<int, fn> b(10);
b.insert(10, SomeFunction);
}
You will see that this compiles and links correctly (I used a function pointer, but a functor with an overloaded operator() would also suffice).
Now if the user's function is changed to one that takes 1 argument, we get a different scenario:
#include <vector>
template <class T, class F>
class Bheap
{
public:
Bheap(int allocateSize) {}
void insert(T value, F f)
{
f(value, value);
}
void getMax();
private:
int sizeUsed;
int allocateSize;
std::vector<T> myBheap;
};
void SomeFunction(int)
{}
int main()
{
typedef void (*fn)(int);
Bheap<int, fn> b(10);
b.insert(10, SomeFunction);
}
The invalid function will make your class fail to compile. Since your template requires a 2 argument function, passing a 1 argument function via pointer causes the error (which you can see here: http://ideone.com/rT7RRa)
For function objects, here is an example of successful compilation:
http://ideone.com/yvWD5o
For unsuccessful compilation:
http://ideone.com/yjeAWB

A functor is a struct providing one or more overloads of operator (), which makes it a choice if you have several comparisons:
// Having a struct X { K key, V value };
struct Less {
template <typename K, typename V>
operator bool () (const X<K, V>& a, const X<K, V>& b> const {
return a.key < b.key;
}
template <typename K, typename V>
operator bool () (const X<K, V>& a, const K& b> const {
return a.key < b;
}
template <typename K, typename V>
operator bool () (const K& a, const X<K, V>& b> const {
return a < b.key;
}
};

Related

Disable to_json and from_json if member variable types don't support nlohmann's json library

I'm using nlohmann's single header json library to serialise a class I wrote. I want to use this class with various types (including but not limited to boost's multiprecision types). The problem is that some types including boost's cpp_bin_float_quad don't support to_json or from_json.
If my class's member variable doesn't have its own to/from_json then the class fails to compile. In this case, users should still be able to use this class's core functionality (member functions, etc) but not let them save/load the class's member variables.
Ideally, if the user needs to save/load the class with their custom types, then they can make the to/from_json functions for their custom types. Preferably the solution will work for C++11.
Here's a mwe to show the issue:
#include "json.hpp"
#include <boost/multiprecision/cpp_bin_float.hpp>
#include <iostream>
template <typename T>
class A {
protected:
T m_x;
public:
A() {m_x = static_cast<T>(1);}
A(T x) : m_x(x) {}
template <typename T1>
friend void to_json(nlohmann::json& j, const A<T1>& a);
template <typename T1>
friend void from_json(const nlohmann::json& j, A<T1>& a);
};
/* I want useless versions of these two functions
* if T doesn't have it's own to/from_json functions! */
template <typename T>
void to_json(nlohmann::json& j, const A<T>& a) {
j = nlohmann::json{{"x", a.m_x}};
}
template <typename T>
void from_json(const nlohmann::json& j, A<T>& a) {
j.at("x").get_to(a.m_x);
}
// no problems with this MY_TYPE
//#define MY_TYPE double
// fails because cpp_bin_float_quad doesn't have to/from_json
#define MY_TYPE boost::multiprecision::cpp_bin_float_quad
int main(){
A<MY_TYPE> a(2);
nlohmann::json js = a;
std::cout << js << std::endl;
auto s2 = js.get<A<MY_TYPE>>();
}
When I try compiling I see errors like these:
mwe.cpp:23:5: error: no matching function for call to ‘nlohmann::json_v3_11_0::basic_json<>::basic_json(<brace-enclosed initializer list>)’
23 | j = nlohmann::json{{"x", a.m_x}};
mwe.cpp:28:19: error: no matching function for call to ‘nlohmann::json_v3_11_0::basic_json<>::get_to(boost::multiprecision::number<boost::multiprecision::backends::cpp_bin_float<113, boost::multiprecision::backends::digit_base_2, void, short int, -16382, 16383>, boost::multiprecision::et_off>&) const’
28 | j.at("x").get_to(a.m_x);
It looks like you want these friend functions not to exist when they don’t make sense.
Try changing the signature of your functions to something like this
template <typename T>
auto to_json(nlohmann::json& j, const A<T>& a) -> decltype(nlohmann::json{{"x", a.m_x}}, void()) {
j = nlohmann::json{{"x", a.m_x}};
}
Looks like we can use SFINAE with std::is_constructible to solve this problem (simply test if the json type can be constructed with). The specific solution I used for my mwe was this:
template <typename T>
class A {
protected:
T m_x;
public:
A() {m_x = static_cast<T>(1);}
A(T x) : m_x(x) {}
template <typename T1,
typename std::enable_if<std::is_constructible<nlohmann::json,T1>::value, bool>::type>
friend void to_json(nlohmann::json& j, const A<T1>& a);
template <typename T1,
typename std::enable_if<std::is_constructible<nlohmann::json,T1>::value, bool>::type>
friend void from_json(const nlohmann::json& j, A<T1>& a);
};
template <typename T,
typename std::enable_if<std::is_constructible<nlohmann::json,T>::value, bool>::type = true>
void to_json(nlohmann::json& j, const A<T>& a) {
j = nlohmann::json{{"x", a.m_x}};
}
template <typename T,
typename std::enable_if<!std::is_constructible<nlohmann::json,T>::value, bool>::type = true>
void to_json(nlohmann::json& j, const A<T>& a) {
throw std::invalid_argument(std::string(typeid(T).name()) + " does not implement nlohmann's to_json");
}
template <typename T,
typename std::enable_if<std::is_constructible<nlohmann::json,T>::value, bool>::type = true>
void from_json(const nlohmann::json& j, A<T>& a){
j.at("x").get_to(a.m_x);
}
template <typename T,
typename std::enable_if<!std::is_constructible<nlohmann::json,T>::value, bool>::type = true>
void from_json(const nlohmann::json& j, A<T>& a){
throw std::invalid_argument(std::string(typeid(T).name()) + " does not implement nlohmann's from_json");
}
Edit: This solution used to depend on nlohmann::detail::has_from_json<nlohmann::json,T>::value. #Human-Compiler explains in the comments why this is a bad idea.

Undefined reference for function specialization in namespace

I'm implementing a JSON archive system modelled after boost::archive. For each type you want to serialize, you define a non-intrusive function that accepts an archive and your object:
// archive.hpp
#pragma once
namespace Archive {
template <class A, class T>
void serialize(A& a, T& value);
}
struct ArchiveOut {
void add(const char* key, int& value) {}
// ... Implementations for basic types ...
template <class T>
void add(const char* key, T& value) {
ArchiveOut archive;
Archive::serialize(archive, value);
}
};
// main.cpp
#include "archive.hpp"
struct Child {
int id;
};
struct Parent {
int id;
Child child;
};
template <class A>
void Archive::serialize(A& a, Parent& v) {
a.add("id", v.id);
a.add("child", v.child);
}
template <class A>
void Archive::serialize(A& a, Child& v) {
a.add("id", v.id);
}
int main() {
Parent parent;
ArchiveOut archive;
Archive::serialize(archive, parent);
}
Right now, the system works for complex nested types but only if serialize is in the global namespace. Once it's moved into the Archive namespace I get a linker error:
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\Users\DDP\AppData\Local\Temp\ccMWEvEu.o:test.cpp:(.text$_ZN10ArchiveOut3addI5ChildEEvPKcRT_[_ZN10ArchiveOut3addI5ChildEEvPKcRT_]+0x20): undefined reference to `void Archive::serialize<ArchiveOut, Child>(ArchiveOut&, Child&)
I know my specializations have the right signature since they match boost's, but maybe my initial prototype is wrong? I've tried digging through the boost internals but can't find where the initial serialize prototype is. I've also checked other answers and all of them are related to specializations not matching the function signature or not placing it in the right namespace. Could I get an explanation for this linker behavior?
You're using overloading where need partial specialization.
The problem is that a function can't be partial specialized.
Suggestion: use static functions inside partial specialized structs.
Something as follows (caution: code not tested)
// archive.hpp
namespace Archive {
template <typename A, typename T>
struct serial;
template <class A, class T>
void serialize(A& a, T& value)
{ serial<A, T>::func(a, value); }
}
// main.cpp
#include "archive.hpp"
// ...
namespace Archive {
template <class A>
struct serial<A, Parent>
{
static void func (A & a, Parent & v)
{
a.add("id", v.id);
a.add("child", v.child);
}
};
template <class A>
struct serial<A, Child>
{
static void func (A & a, Child & v)
{ a.add("id", v.id); }
};
}

Error in passing function to function template

I have function templates :
template<typename T>
inline T fun3(T &x1, T &x2)
{
return std::pow(x1,2.0) + std::pow(x2,2.0);
}
template<typename T, typename U>
inline T fun5(U &a)
{
return (T(4.0+a*(-2.0),5.0+ a*3.0));
}
template<typename F, typename T>
void min(F fun1, T& v)
{
double x={10.0};
v=fun1(x);
}
int main()
{
double val;
min(fun3(fun5),val);
std::cout<<"value = "<<val<<"\n";
return 0;
}
I want to evaluate fun3(fun5(x)) and have functions as shown above. But getting error as no matching function for call to ‘Function5<double>::fun5(<unresolved overloaded function type>)’ obj1(o5.fun5(o3.fun3),-2.0,0.0,location,value);
Can someone explain how can I pass function to min()?
What will change if all these functions were class templates like:
template<typename T>
class Fun3 {
inline T fun3(T &x1, T &x2)
{
return std::pow(x1,2.0) + std::pow(x2,2.0);
}
};
template<typename T, typename U>
class Fun5 {
inline T fun5(U &a)
{
return (T(4.0+a*(-2.0),5.0+ a*3.0));
}
};
template<typename F, typename T>
class Min {
void min(F fun1, T& v)
{
double x={10.0};
v=fun1(x);
}
};
int main()
{
double val;
Fun5<double> o5;
Fun3<decltype (o5.fun5)> o3;
Min<???,decltype (o5.fun5)> obj; //What is here?
obj(o3.fun3(o5.fun5),val);
std::cout<<"value = "<<val<<"\n";
return 0;
}
I don't know what will go to commented line.
How can I use a function object (functor) here?
I want to evaluate fun3(fun5(x))
min([](auto x){ return fun3(fun5(x)); }, val);
There's no function composition in C++ standard library (though it can be defined with some effort.)
If you really want fun, at least try lambdas. They are simple.
I'd say stay away from templates in the way you want to use them. I am assuming you want a simple happy life to focus on productive thing and I may be wrong. Pardon.
Still, I worked on your code a bit and would say that don't confuse template and macros. It looks like the case at least to me.
Note that the template actually instantiate the code and for that all you can pass is arguments to whatever types and specify those types while template instantiating.
Here is a code sample at ideone - not exactly same but to show how something can be done.
For min(fun3(fun5),val);
If you really want fun3 behavior, pass it. Dont expect the result to be passed just like it works for macro.
.
#include <iostream>
#include <cmath>
using namespace std;
typedef double (*_typeofFun1)(double&);
typedef double (*_typeofFun3)(double&, double&);
template<typename T>
T fun3(T &x1, T &x2)
{
return std::pow(x1,2.0) + std::pow(x2,2.0);
}
template<typename T, typename U>
U fun5(T t, U &a)
{
//return (T(4.0+a*(-2.0),5.0+ a*3.0));
return t(a,a);
}
template <typename T>
T fun1Param(T& arg)
{
return 2*arg;
}
template<typename F, typename T>
void min(F fun1, T& v)
{
double x={10.0};
v=fun1(x);
}
int main()
{
double val = 1.0;
double d = fun5<_typeofFun3, double> (fun3, val);
fun3<double>(d, val);
min<_typeofFun1>(fun1Param,val);
std::cout<<"value = "<<val<<"\n";
return 0;
}

C++ wrapper around any collection type using templates

Extremely new to c++ however have a question regarding templates
Suppose I have a simple template class as defined below:
template<typename Collection>
class MySack {
private:
Collection c;
public:
typedef typename Collection::value_type value_type;
void add(const value_type& value) {
c.push_back(value);
}
};
The aim of the class being to accept any type of collection, and allow a user to insert the correct type of value for the specified typename Collection.
The obvious problem is that this is only going to work for types which have a push_back method defined, which means it would work with list however not with set.
I started reading about template specialization to see if that'd be any help, however I don't think this would provide a solution as the type contained within the set would have to be known.
How would this problem be approached in c++?
You can use std::experimental::is_detected and if constexpr to make it work:
template<class C, class V>
using has_push_back_impl = decltype(std::declval<C>().push_back(std::declval<V>()));
template<class C, class V>
constexpr bool has_push_back = std::experimental::is_detected_v<has_push_back_impl, C, V>;
template<typename Collection>
class MySack {
private:
Collection c;
public:
typedef typename Collection::value_type value_type;
void add(const value_type& value) {
if constexpr (has_push_back<Collection, value_type>) {
std::cout << "push_back.\n";
c.push_back(value);
} else {
std::cout << "insert.\n";
c.insert(value);
}
}
};
int main() {
MySack<std::set<int>> f;
f.add(23);
MySack<std::vector<int>> g;
g.add(23);
}
You can switch to insert member function, which has the same syntax for std::vector, std::set, std::list, and other containers:
void add(const value_type& value) {
c.insert(c.end(), value);
}
In C++11, you might also want to create a version for rvalue arguments:
void add(value_type&& value) {
c.insert(c.end(), std::move(value));
}
And, kind-of simulate emplace semantics (not truly in fact):
template <typename... Ts>
void emplace(Ts&&... vs) {
c.insert(c.end(), value_type(std::forward<Ts>(vs)...));
}
...
int main() {
using value_type = std::pair<int, std::string>;
MySack<std::vector<value_type>> v;
v.emplace(1, "first");
MySack<std::set<value_type>> s;
s.emplace(2, "second");
MySack<std::list<value_type>> l;
l.emplace(3, "third");
}
I started reading about template specialization to see if that'd be
any help, however I don't think this would provide a solution as the
type contained within the set would have to be known.
You can partially specialize MySack to work with std::set.
template <class T>
class MySack<std::set<T>> {
//...
};
However, this has the disadvantage that the partial specialization replaces the whole class definition, so you need to define all member variables and functions again.
A more flexible approach is to use policy-based design. Here, you add a template parameter that wraps the container-specific operations. You can provide a default for the most common cases, but users can provide their own policy for other cases.
template <class C, class V = typename C::value_type>
struct ContainerPolicy
{
static void push(C& container, const V& value) {
c.push_back(value);
}
static void pop(C& container) {
c.pop_back();
}
};
template <class C, class P = ContainerPolicy<C>>
class MySack
{
Collection c;
public:
typedef typename Collection::value_type value_type;
void add(const value_type& value) {
P::push(c, value);
}
};
In this case, it is easier to provide a partial template specialization for the default policy, because it contains only the functionality related to the specific container that is used. Other logic can still be captured in the MySack class template without the need for duplicating code.
Now, you can use MySack also with your own or third party containers that do not adhere to the STL style. You simply provide your own policy.
struct MyContainer {
void Add(int value);
//...
};
struct MyPolicy {
static void push(MyContainer& c, int value) {
c.Add(value);
}
};
MySack<MyContainer, MyPolicy> sack;
If you can use at least C++11, I suggest the creation of a template recursive struct
template <std::size_t N>
struct tag : public tag<N-1U>
{ };
template <>
struct tag<0U>
{ };
to manage precedence in case a container can support more than one adding functions.
So you can add, in the private section of your class, the following template helper functions
template <typename D, typename T>
auto addHelper (T && t, tag<2> const &)
-> decltype((void)std::declval<D>().push_back(std::forward<T>(t)))
{ c.push_back(std::forward<T>(t)); }
template <typename D, typename T>
auto addHelper (T && t, tag<1> const &)
-> decltype((void)std::declval<D>().insert(std::forward<T>(t)))
{ c.insert(std::forward<T>(t)); }
template <typename D, typename T>
auto addHelper (T && t, tag<0> const &)
-> decltype((void)std::declval<D>().push_front(std::forward<T>(t)))
{ c.push_front(std::forward<T>(t)); }
Observe that the decltype() part enable they (through SFINAE) only if the corresponding method (push_back(), insert() or push_front()) is enabled.
Now you can write add(), in the public section, as follows
template <typename T>
void add (T && t)
{ addHelper<C>(std::forward<T>(t), tag<2>{}); }
The tag<2> element make so the tag<2> addHelper() method is called, if available (if push_back() is available for type C), otherwise is called the tag<1> method (the insert() one) if available, otherwise the tag<0> method (the push_front() one) is available. Otherwise error.
Also observe the T && t and std::forward<T>(t) part. This way you should select the correct semantic: copy or move.
The following is a full working example
#include <map>
#include <set>
#include <list>
#include <deque>
#include <vector>
#include <iostream>
#include <forward_list>
#include <unordered_map>
#include <unordered_set>
template <std::size_t N>
struct tag : public tag<N-1U>
{ };
template <>
struct tag<0U>
{ };
template <typename C>
class MySack
{
private:
C c;
template <typename D, typename T>
auto addHelper (T && t, tag<2> const &)
-> decltype((void)std::declval<D>().push_back(std::forward<T>(t)))
{ c.push_back(std::forward<T>(t)); }
template <typename D, typename T>
auto addHelper (T && t, tag<1> const &)
-> decltype((void)std::declval<D>().insert(std::forward<T>(t)))
{ c.insert(std::forward<T>(t)); }
template <typename D, typename T>
auto addHelper (T && t, tag<0> const &)
-> decltype((void)std::declval<D>().push_front(std::forward<T>(t)))
{ c.push_front(std::forward<T>(t)); }
public:
template <typename T>
void add (T && t)
{ addHelper<C>(std::forward<T>(t), tag<2>{}); }
};
int main ()
{
MySack<std::vector<int>> ms0;
MySack<std::deque<int>> ms1;
MySack<std::set<int>> ms2;
MySack<std::multiset<int>> ms3;
MySack<std::unordered_set<int>> ms4;
MySack<std::unordered_multiset<int>> ms5;
MySack<std::list<int>> ms6;
MySack<std::forward_list<int>> ms7;
MySack<std::map<int, long>> ms8;
MySack<std::multimap<int, long>> ms9;
MySack<std::unordered_map<int, long>> msA;
MySack<std::unordered_multimap<int, long>> msB;
ms0.add(0);
ms1.add(0);
ms2.add(0);
ms3.add(0);
ms4.add(0);
ms5.add(0);
ms6.add(0);
ms7.add(0);
ms8.add(std::make_pair(0, 0L));
ms9.add(std::make_pair(0, 0L));
msA.add(std::make_pair(0, 0L));
msB.add(std::make_pair(0, 0L));
}

C++ - want virtual acting solely as redirection

Let's say I have a template:
template <class N, class I>
void add(N* element, std::list<N*> & container, I (N::*f)() const,
std::string successmsg, std::string exceptmsg) {
//...
}
And I want to call it for a list of Base Class pointers to a derivative class.
add(newAirplane, airplanes, &Airplane::getRegistration,
"Added!", "Error: Existent!");
Airplane inherits from AirplaneType.
Of course, it doesn't compile, N is first defined as AirplaneType and then as Airplane.
I added a virtual getRegistration # AirplaneType but of course, the compiler gives out a vtable error.
What's the proper way to solve this? AirplaneType has no registration attribute and I'm not interested in it having one. I also wanted to avoid virtual getRegistration() const {return "";}
Any suggestions for good practice?
EDIT:
Thanks for answers, but still not working. I think I have found the remaining problem, but not its solution:
void Airline::addAirplane(AirplaneType* airplane) {
add(newAirplane, airplanes, &Airplane::getRegistration,
"Added!", "Error: Existent!");
}
The type of pointer received is AirplaneType, not Airplane.
airplanes is a list of AirplaneType pointers.
You need another template parameter, because you care about two different classes - the type of the pointer (and hence the member function you're going to call with it), and the type of the container:
#include <list>
struct AirplaneType {
};
struct Airplane : AirplaneType {
int check() const { return 3; }
};
template <typename T, typename U, typename I>
void add(T* element, std::list<U*> & container, I (T::*f)() const) {
container.push_back(element);
I i = (element->*f)();
}
int main() {
std::list<AirplaneType*> ls;
Airplane a;
add(&a, ls, &Airplane::check);
}
In this case my add function doesn't really use the fact that container is a list, so a more sensible version might be:
template <typename T, typename U, typename I>
void add(T* element, U & container, I (T::*f)() const) {
container.push_back(element);
I i = (element->*f)();
}
And then again, you could abstract further:
template <typename T, typename U, typename AUF>
void add(T element, U & container, AUF func) {
container.push_back(element);
typename AUF::result_type i = func(element);
}
... but that's slightly less convenient for the caller:
#include <functional>
add(&a, ls, std::mem_fun(&Airplane::check));
Any suggestions for good practice?
Don't create containers of raw pointers.
Edit: to get this working with a virtual function, with each of my options:
#include <list>
#include <functional>
#include <iostream>
struct AirplaneType {
virtual int check() const { return 0; }
};
struct Airplane : AirplaneType {
int check() const { std::cout << "check\n"; return 3; }
};
template <typename T, typename U, typename I>
void add(U* element, std::list<T*> & container, I (U::*f)() const) {
container.push_back(element);
I i = (element->*f)();
}
template <typename T, typename U, typename AUF>
void add2(T element, U & container, AUF func) {
container.push_back(element);
typename AUF::result_type i = func(element);
}
int main() {
std::list<AirplaneType*> ls;
Airplane a;
add(static_cast<AirplaneType*>(&a), ls, &AirplaneType::check);
add2(&a, ls, std::mem_fun(&AirplaneType::check));
}
Output is:
check
check
which shows that the override is correctly called even though the function pointer was taken to AirplaneType::check, not Airplane::check.
You need to add an additional template parameter for the common base since C++ does not handle contravariant types. That is, std::list<Airplane*> is an entirely different type from std::list<AirplaneType*>, and no implicit conversion can occur from the list of pointers to the most derived to the least derived.. So, effectively your add function would need to become:
template <class N, class I, class B>
void add(N* element, std::list<B*> & container, I (N::*f)() const,
std::string successmsg, std::string exceptmsg)