I am working with std::priority_queue for the first time for a University assignment. The assignment is a simulation of process scheduling. I would like to pass a parameter to my Comparison struct constructor to initialise and I thought I saw it on another forum, but I'm unable to find the source again. I looked on SO before posting but I didn't see anything similar.
Here is my priority_queue:
/* schedules.hpp / .cpp */
#include "process.hpp"
namespace my = procschedassignment;
int tick = 0;
std::priority_queue<my::Process, _
std::vector<my::Process>,
PrioritiseHighestResponseRatioNext(tick) > rq;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
line 100 - compiler errors are here
// ...
Here is my Comparison struct:
/* prioritise_process.hpp / .cpp */
#include "process.hpp"
namespace my = procschedassignment;
struct PrioritiseHighestResponseRatioNext {
public:
explicit PrioritiseHighestResponseRatioNext(int const& cpu_time)
: cpu_time_(cpu_time) {};
bool PrioritiseHighestResponseRatioNext::operator()(my::Process const& lhs,
my::Process const& rhs) {
bool ret;
if (lhs.wait_time() > cpu_time_) {
ret = (lhs.ResponseRatio() > rhs.ResponseRatio());
} else if (rhs.wait_time() > cpu_time_) {
ret = (lhs.ResponseRatio() < rhs.ResponseRatio());
}
return ret;
};
private:
const int cpu_time_;
};
The compiler errors I get with this code are:
../src/schedules.cpp:100: error: ‘time’ cannot appear in a constant-expression
../src/schedules.cpp:100: error: template argument 3 is invalid
../src/schedules.cpp:100: error: invalid type in declaration before ‘;’ token
Is it possible to have a parameterised Comparison struct with std::priority_queue?
I am new to STL so I apologise that I don't have a better understanding of what's happening here.
You are trying to pass an object as template-parameter. This will not work. You should provide your comparator as argument to the constructor, and the type of the comparator as template-argument.
// declare type
typedef std::priority_queue<my::Process,
std::vector<my::Process>,
PrioritiseHighestResponseRatioNext > process_queue;
// ^^^ just a type, no object ^^^
// create object
process_queue rq(PrioritiseHighestResponseRatioNext(tick));
Related
I have a templated class to aid in compile time computation of physical quantities. It uses the extra template parameters (std::ratio) to ensure things like a Length can only be added to a Length, or that Area is a Length times a Length.
#include <ratio>
template <
typename Length = std::ratio<0>, // Meter
typename Mass = std::ratio<0>, // Kilogram
typename Time = std::ratio<0>, // Second
typename Current = std::ratio<0>, // Ampere
typename Temperature = std::ratio<0>, // Kelvin
typename Amount = std::ratio<0>, // Mole
typename Luminous = std::ratio<0> // Candela
>
class Quantity {
private:
double value;
public:
constexpr Quantity(double val) : value(val) {}
Quantity &operator+=(Quantity const &that) {
value += that.value;
return *this;
}
// ...
};
But sometimes I want to convert back to a simple double, for interfacing with other stuff.
I could add a member function for the templated class that returns the internal double - or enables implicit (or explicit) conversion to double when a double is needed.
constexpr double getValue() { return value; }
constexpr operator double() { return value; }
However, I really only want this implicit conversion to happen when the "dimensions" of the quantity are all 0 (all template parameters are 0).
I could just declare the same member functions and only define the specialization that I want. But that still declares that the conversion exists for types that I don't ever want to allow conversion from (you should divide by your desired units first). This makes my editor tell me it's ok but it won't link at compiletime.
Is there a way to declare member functions only on certain specializations?
Of note, I'm stuck on C++14 for now, otherwise I think an if constexpr could work...
No, if constexpr cannot be used to provide for conditional definition of class methods. if constexpr belongs in some method or a function, so that needs to be declared before anything can be done with if constexpr, and your goal is to not even declare it in the first place.
There is no way to directly instantiate a class method only for certain specializations or template instances, however there's a common approach that comes pretty close: simulate an overload resolution failure.
Here's a simplified example:
#include <type_traits>
#include <iostream>
template<typename T>
struct life {
template<typename=
typename std::enable_if<std::is_same<T,int>::value>::type>
constexpr int answer()
{
return 42;
}
};
int main()
{
life<int> of_brian;
std::cout << of_brian.answer() << std::endl; // Shows 42
life<double> of_black_night;
std::cout << of_black_night.answer() << std::endl; // Error
return 0;
}
The template class effectively implements answer() only for its int instance. gcc's error message, for attempting to invoke it from an undesirable instance of the template is:
error: no matching function for call to ‘life<double>::answer()’
which is a pretty close facsimile for "this doesn't exist, pal".
This is logically equivalent to what you're attempting to do with your template, the only difference is that you need to check a bunch of template parameters, instead of just one.
This question is a follow-up of another question: How to realize automatic type conversion for template methods?.
If a type conversion is needed within a template method, can I somehow tell the compiler how to do just that conversion itself?
I know two possibilities to code the conversion:
template specialization
providing a conversion overload (which has been the answer of the former question).
Both are fine, but may require writing boilerplate code. Is there a way to "inject" just the type conversion code and let the compiler do the rest?
Please see the following example code. I need to provide a conversion from std::string to my Setting class. How could I tell the compiler how to convert it?
#include <string>
#include <vector>
class Setting
{
public:
Setting(int)
{
}
Setting(double)
{
}
// It is not possible to provide a constructor taking std::string as argument,
// because this code is within an external libary!!!
};
// Is is not possible to create an overload of this method, since in real life
// it is a class member function within an external library.
//
void storeSetting(const Setting&)
{
// Storing setting...
}
// Template method that works with int, double and float, because Settings can
// be created from these basic types. But the method will not work
// for std::string, since there is no appropriate constructor.
template <typename Type>
void storeAll(std::vector<Type> elements)
{
// A lot of lengthy storage preparation code
// ...
//
// Final Storage
for (const Type& element : elements)
{
storeSetting(element);
}
}
// Solution by template specialization
template <>
void storeAll(std::vector<std::string> elements)
{
// A lot of lengthy storage preparation code
// ...
//
// Final Storage
for (const std::string& element : elements)
{
storeSetting(stoi(element));
}
}
// Solution by providing a conversion overload
//
// TODO: When containers are concerned, this is not handy.
// I dont have to repeat the "lengthy storage preparation code".
// On the other hand, the conversion code is lengthy boilerplate code itself.
// Is there another way to "inject" a user-defined type conversion?
void storeAll(std::vector<std::string> elements)
{
std::vector<int> convertedElements;
for (const std::string& element : elements)
{
convertedElements.push_back(stoi(element));
}
storeAll(convertedElements);
}
int main()
{
std::vector<double> numbers1 = {1.0, 2.0, 3.0};
std::vector<int> numbers2 = {2, 3, 4};
std::vector<float> numbers3 = {3.0, 4.0, 5.0};
storeAll(numbers1);
storeAll(numbers2);
storeAll(numbers3);
std::vector<std::string> numbers4 = {"4", "5", "6"};
storeAll(numbers4);
return 0;
}
You cannot add implicit conversion between types you don't own.
But you can still create function which does the conversion for you:
// The forwarding one for exisiting contructor
template <typename ... Ts>
auto makeSetting(const Ts&... args)
-> decltype(Setting{args...})
{
return Setting{args...};
}
// Optionally, one to avoid unwanted copy constructor
const Setting& makeSetting(const Setting& s) { return s; }
// Your extra version
Setting makeSetting(const std::string& s)
{
return Setting{std::stoi(s)};
}
Then in your generic function:
template <typename T>
void storeAll(std::vector<T> elements)
{
// A lot of lengthy storage preparation code
// ...
// Final Storage
for (const auto& element : elements)
{
storeSetting(makeSetting(element));
}
}
I can't pass (custom) priority_queue reference. Priority queue was customized using lambda. Is there any workaround? I tried using functors and all that, but none of them would let me go past line with the priority_queue creation at all without failing on the compilation step with various issues where priority_queue constructor wouldn't accept the sorting method. I guess it cannot reach the lambda or it just needs some special type declaration in the header, but I can't figure it out
Here is a simplified version of my code.
#include <queue>
#include <memory>
struct Node
{
int full_dist,
dist1,
dist2;
Node(int d1, int d2) { full_dist = d1 + d2; }
};
void some_processing(std::priority_queue<std::shared_ptr<Node>>& nodes)
{
//something being done with the queue
}
int main()
{
auto comp = [] (const std::shared_ptr<Node>& l, const std::shared_ptr<Node> r) -> bool
{ return l->full_dist > r->full_dist; };
std::priority_queue<std::shared_ptr<Node>, std::vector<std::shared_ptr<Node>>, decltype(comp)> nodes(comp);
some_processing(nodes);//breaks here
}
Here is the error I've got on this example:
test.cpp:24:24: error: invalid initialization of reference of type ‘std::priority_queue<std::shared_ptr<Node> >&’
from expression of type ‘std::priority_queue<std::shared_ptr<Node>, std::vector<std::shared_ptr<Node>, std::allocator<std::shared_ptr<Node> > >, main()::__lambda0>’
some_processing(nodes);
Make the function templated on the comparison type.
template<typename CompT>
void some_processing(
std::priority_queue<std::shared_ptr<Node>,
std::vector<std::shared_ptr<Node>>,
CompT> & nodes)
{
// something being done with the queue
}
Or keep things simple and just make the whole container type templated.
template<typename QueueT>
void some_processing(QueueT& nodes)
{
// something being done with the queue
}
Your priority queue is
std::priority_queue<std::shared_ptr<Node>,
std::vector<std::shared_ptr<Node>>, decltype(comp)>
That's it's declared type. The parameter to your function is a reference to:
std::priority_queue<std::shared_ptr<Node>>
This is a completely different type. You can't pass a reference to one type to a function that expects a reference to a completely different type as a parameter. Each instance of a template class is unique. The difference between the first and the second class, here, is the same difference as a difference between class A and class B.
I took my first C++ class in 1990, long before your newfangled exceptions, STL and whatnot. Now I am writing a custom C++ container and I decided I would use this as an opportunity to learn some C++11 techniques and concepts, especially unique_ptr. Unfortunately I am having some trouble with the move semantics (I think) when inserting an element. Here is a very stripped down version of the code I am trying to get to compile:
#include <vector>
#include <memory>
struct Key {
int k_;
Key() : k_(0){};
explicit Key(int k) : k_(k){};
Key(const Key &o) : k_(o.k_) {}
Key(Key &&o) { k_ = std::move(o.k_); }
Key &operator=(const Key &o) {
k_ = o.k_;
return *this;
}
Key &operator=(Key &&o) {
k_ = std::move(o.k_);
return *this;
}
int get() const { return k_; }
};
template <class T> class CustomContainer {
public:
typedef std::pair<Key, std::unique_ptr<Key>> Record;
CustomContainer() {}
~CustomContainer(){};
bool insert(const Record &record) {
objects.emplace_back(std::move(record));
return true;
}
std::vector<Record> objects;
};
int main() {
CustomContainer<Key> q;
q.insert(CustomContainer<Key>::Record(Key(1), std::unique_ptr<Key>(new Key(1))));
}
I am inserting a pointer to a Key object to keep the code simple. In my actual application, Key is a little more complicated, T is not Key, and the Custom container has many more member functions, but this is enough to highlight the problem. When I just have a unique_ptr object in the vector, everything appears to work just fine. As soon as I add the pair, I get:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/ext/new_allocator.h:120:23: error: call to
implicitly-deleted copy constructor of 'std::pair<Key, std::unique_ptr<Key, std::default_delete<Key> > >'
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
.
.
.
simple.cc:33:13: note: in instantiation of function template specialization 'std::vector<std::pair<Key,
std::unique_ptr<Key, std::default_delete<Key> > >, std::allocator<std::pair<Key, std::unique_ptr<Key,
std::default_delete<Key> > > > >::emplace_back<const std::pair<Key, std::unique_ptr<Key,
std::default_delete<Key> > > >' requested here
objects.emplace_back(std::move(record));
^
simple.cc:41:5: note: in instantiation of member function 'CustomContainer<Key>::insert' requested here
q.insert(CustomContainer<Key>::Record(Key(1), std::unique_ptr<Key>(new Key(1))));
I tried the same thing with a custom class instead of a pair and got the same error. I can't seem to get the compiler to call the move constructor instead of the copy constructor no matter how many std::move()s I add. What am I missing?
you're passing a const ref to a unique_ptr and then trying to copy from it (you can only move from a non-const). Pass the entire object and then move from that. Since you're initialising with a temporary (r-value reference), there will be an implicit move at the call site.
patch to fix your code is here:
template <class T> class CustomContainer {
public:
...
bool insert(Record record) { // <-- FIXED
...
}
};
I'm in the process of refactoring a large class -- let's call it Big -- that has a huge amount of copy-paste code. Much of this copy-paste code exists in switch cases where only the types involved end up being different. The code is switching based on an enum member variable of the class whose value is known only at runtime.
My attempt to fix this involves having a Dispatcher class that looks up appropriately typed functions via a static function called lookup(). The functions that do the actual work are always called go() and have to be defined in a wrapper class template (whose sole parameter is the runtime enum value currently being switched on). The go() functions may or may not be template functions themselves.
Here is a distilled version of the code. My apologies for the length, but this was as short as I could get it without losing important context.
#include <cassert>
class Big
{
public:
enum RuntimeValue { a, b };
Big(RuntimeValue rv) : _rv(rv) { }
bool equals(int i1, int i2)
{
return Dispatcher<Equals, bool(int, int)>::lookup(_rv)(i1, i2);
}
template<typename T>
bool isConvertibleTo(int i)
{
return Dispatcher<IsConvertibleTo, bool(int)>::lookup<T>(_rv)(i);
}
private:
template<RuntimeValue RV>
struct Equals
{
static bool go(int i1, int i2)
{
// Pretend that this is some complicated code that relies on RV
// being a compile-time constant.
return i1 == i2;
}
};
template<RuntimeValue RV>
struct IsConvertibleTo
{
template<typename T>
static bool go(int i)
{
// Pretend that this is some complicated code that relies on RV
// being a compile-time constant.
return static_cast<T>(i) == i;
}
};
template<template<RuntimeValue> class FunctionWrapper, typename Function>
struct Dispatcher
{
static Function * lookup(RuntimeValue rv)
{
switch (rv)
{
case a: return &FunctionWrapper<a>::go;
case b: return &FunctionWrapper<b>::go;
default: assert(false); return 0;
}
}
template<typename T>
static Function * lookup(RuntimeValue rv)
{
switch (rv)
{
case a: return &FunctionWrapper<a>::go<T>;
case b: return &FunctionWrapper<b>::go<T>;
default: assert(false); return 0;
}
}
// And so on as needed...
template<typename T1, typename T2>
static Function * lookup(RuntimeValue rv);
};
RuntimeValue _rv;
};
int main()
{
Big big(Big::a);
assert(big.equals(3, 3));
assert(big.isConvertibleTo<char>(123));
}
This mostly works, except that:
It builds and works fine under Visual C++ 9 (2008), but under GCC 4.8 it results in compilation errors in the function-template overload of lookup().
It requires that a new function-template overload of lookup() be written for every new number of function template parameters that we want to support in go().
It's cumbersome and confusing to use.
Here are the errors that occur under GCC:
Big.cpp: In static member function 'static Function* Big::Dispatcher<FunctionWrapper, Function>::lookup(Big::RuntimeValue)':
Big.cpp(66,65) : error: expected primary-expression before '>' token
case a: return &FunctionWrapper<a>::go<T>;
^
Big.cpp(66,66) : error: expected primary-expression before ';' token
case a: return &FunctionWrapper<a>::go<T>;
^
Big.cpp(67,65) : error: expected primary-expression before '>' token
case b: return &FunctionWrapper<b>::go<T>;
^
Big.cpp(67,66) : error: expected primary-expression before ';' token
case b: return &FunctionWrapper<b>::go<T>;
^
My question is twofold:
Why is this failing to build under GCC, and how do I fix it?
Is there a better (i.e., less cumbersome and confusing) way to do this?
The code has to be compilable under Visual C++ 9 (2008), so I can't use anything C++11-specific.
Since go is a dependent name of a template, you need to use the template disambiguator:
case a: return &FunctionWrapper<a>::template go<T>;
// ^^^^^^^^
case b: return &FunctionWrapper<b>::template go<T>;
// ^^^^^^^^
This tells the compiler to parse what follows the scope resolution operator (::) as the name of a template, and the subsequent angular brackets as delimiters for the template arguments.
Why is this failing to build under GCC, and how do I fix it?
Because GCC is conforming to the Standard, and performs two-phase name lookup, while MSVC delays name lookup until instantiation time and, therefore, knows that go is the name of a template.
Before instantiation this information is not available, because it is impossible to know what T is, and the primary template could be specialized for a given T so that go is not the name of a member function template, but rather of a data member.
This said, I expect MSVC to support the template disambiguator anyway, so adding it should make your program compile both on GCC/Clang/whatever-conforms-to-the-Standard and on MSVC.
I recently wrote a command dispatcher:
#include <map>
// because std::invoke is not in this compiler version.
#define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember))
template <class MyType, class cmd_type, class ret_type, typename... Args>
class CommandDispatcher {
typedef ret_type (MyType::*CommandFunction)(Args... args);
public:
// create using static/existing map
CommandDispatcher(std::map<cmd_type, CommandFunction>& cmd_map) : _command_table(cmd_map) {}
ret_type operator()(MyType& my_obj, cmd_type cmd, Args... args)
{
int retval = 0;
if (_command_table.find(cmd) == _command_table.end()) {
std::cerr << "No command implementation found: " << cmd << endl;
return -EINVAL;
}
return CALL_MEMBER_FN(my_obj, _command_table[cmd])(args...);
}
private:
std::map<cmd_type, CommandFunction>& _command_table;
};
Using it looks like:
class MyClass {
public:
MyClass() : _dispatcher(_command_map) {}
private:
static std::map<int, CommandFunction> _command_map;
CommandDispatcher<MyClass, int, int, const char*, int> _dispatcher;
};
And in cpp:
std::map<int, CommandFunction> MyClass::_command_map{
{E_CMD1, &MyClass::Cmd1},
{E_CMD2, &MyClass::Cmd2},
};