How to declare a std::chrono::duration<double> variable in c++ - c++

I am working on a code where I am calculating the duration of time and then saving it in list.
auto start_time = std::chrono::high_resolution_clock::now();
/*
*some code here
*/
auto finish_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> time_duration = finish_time - start_time ;
Now here I need to save time_duration in a list at a given index. If that given index, already contains a value, I need to add time_duration with the current value of that index and save it. For this I have below list and code:
list <std::chrono::duration<double>> dTimeList;
auto iter = dTimeList.begin();
advance(iter, 0);
*iter+= time_duration; //Error at this line
But running above code I get below error:
Most probably this error is coming because I have empty list with no items in it. This is why I thought of adding a item at 0th index like below:
auto itr = dTimeList.begin();
advance(itr, 0);
std::chrono::duration<double> t = 0.0;
dTimeList.insert(itr, t);
but again above is also giving below error. How can I resolve this issue. Thanks
No suitable constructor available to convert double to std::chrono::duration<double>

Probably you should not use list to store data at index. Try to use std::unordered_map instead.
#include <unordered_map>
auto start_time = std::chrono::high_resolution_clock::now();
/*
*some code here
*/
auto finish_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> time_duration = finish_time - start_time ;
typedef int YourObjectIdType;
std::unordered_map<YourObjectIdType, std::chrono::duration<double>> dTimes;
and now insert or update item like this:
dTimes[objectId] += time_duration;
or like this:
auto it = dTimes.find(objectId);
if (it != dTimes.end()) {
*it += time_duration;
} else {
dTimes.insert(std::make_pair(objectId, time_duration));
}
also, you can use std::map the same way, it slightly slower, but [begin(), end()) range is sorted.

You're trying to write to the end iterator. When the list is empty, dTimeList.begin() == dTimeList.end(), so you can't write to it.
You need to check if the index exists first, and extend the list otherwise:
void add_time_to_list(
std::list<std::chrono::duration<double>>& l,
const std::chrono::duration<double>& t,
std::size_t index = 0
) {
if (index < l.size()) {
*std::next(l.begin(), index) += t;
} else if (index == l.size()) {
// New element; add to back
l.push_back(t);
} else {
// Here you can throw an error that index is too big
// or append 0 until it gets to the desired size or something
}
}
Note that accessing arbitrary indexes like this could mean that you don't want a std::list. This would be easier with a std::vector (*std::next(l.begin(), index) becomes l[index]). I would also suggest a std::map, where the function can just be written as:
void add_time_to_list(
std::map<std::size_t, std::chrono::duration<double>>& m,
const std::chrono::duration<double>& t,
std::size_t index = 0
) {
m[index] += t; // if m[index] doesn't exist, it is default constructed.
}

No suitable constructor available to convert double to std::chrono::duration
I dare to suggest dealing with std::list is far from a core issue here.
Dealing with std::chrono, one should know the foundation concepts of it. For what is not explained here please look it up on cppreference.com
Step one. You decide which Clock you need/want to use.
using Clock = typename std::chrono::high_resolution_clock ;
Step two. You use the duration nested type, declared in it. In your use-case you would like to have a sequence of durations.
using time_durations_sequence_type = std::vector<Clock::duration> ;
type naming is very important. I deliberately use the generic term 'sequence'. You might push the idea of std::list to implement it, but I fail to see why. Thus I am using std::vector above.
Also notice, I do not use double or long int as the "duration" type. Firstly std::duration is a "time taken for event to happen". Second, it is not a scalar it is a class type. Traditionaly, from C years, time concept was based just on ticks.
So, to cut the long story short we use the std::duration concept , concretized as a nested type in the "clock" we have selected to use here.
// not a point in time
Clock::duration
//
// there is no guarantee above
// is the same type as
// std::chrono::duration<double>
// and we do not need one
And above is all we need to proceed to implement the functionality you require.
// two points in time
auto start_time = Clock::now();
auto finish_time = Clock::now();
// the duration
Clock::duration time_duration = finish_time - start_time ;
Now we store the duration in our sequence type.
time_durations_sequence_type tdst{ time_duration } ;
And elsewhere we use the sequence of stored durations we have accumulated.
// time_durations_sequence_type tdst
for (auto duration : tdst ) {
std::cout << std::endl
<< duration.count() << " nano seconds"
<< std::endl;
}
Notice how above we used the count() method to get to the actual ticks, which by std::chrono default, do represent nanoseconds.
Working code is here .

Related

Optimized argmin: an effective way to find an item minimizing a function

Let us say I've got a collection of items and a score function on them:
struct Item { /* some data */ };
std::vector<Item> items;
double score(Item);
I'd like to find the item from that collection whose score is the lowest. An easy way to write this is:
const auto argmin = std::min_element(begin(items), end(items), [](Item a, Item b) {
return score(a) < score(b);
});
But if score is a heavy-to-compute function, the fact that std::min_element actually calls it multiple times on some items may be worrying. And this is expected because the compiler cannot guess score is a pure function.
How could I find argmin but with score being called only once per item? Memoization is one possibility, anything else?
My objective is to write a code snippet which is easy to read, in a dream world as obvious as calling std::min_element on the collection is.
As I commented above, if the vector is not too big, you can use std::transform to store all scores first, then apply std::min_element.
However, if you want to take benefit of "lazy evaluation", and still want to use C++'s STL, there are some tricks to work it out.
The point is std::accumulate can be regarded as a general reduce or fold operation (like foldl in haskell). With C++17's syntax sugar for std::tuple, we can write something like:
auto [min_ind, _, min_value] = std::accumulate(items.begin(), items.end(),
std::make_tuple(-1LU, 0LU, std::numeric_limits<double>::max()),
[] (std::tuple<std::size_t, std::size_t, double> accu, const Item &s) {
// up to this point, the index of min, the current index, and the last minimal value
auto [min_ind, cur_ind, prev_min] = accu;
double r = score(s);
if ( r < prev_min ) {
return std::make_tuple(cur_ind, cur_ind + 1, r);
} else {
return std::make_tuple(min_ind, cur_ind + 1, prev_min);
}
});
Here's a function that does what you want--even going beyond the intuitive "call score exactly once per element" by realizing that there's nothing smaller than negative infinity!
const Item* smallest(const std::vector<Item>& items)
{
double min_score = items.empty() ? NAN : INFINITY;
const Item* min_item = items.empty() ? nullptr : &*begin(items);
for (const auto& item : items) {
double item_score = score(item);
if (item_score < min_score) {
min_score = item_score;
min_item = &item;
if (item_score == -INFINITY) {
break;
}
}
}
return min_item;
}
As suggested bu user #liliscent, one could:
generate a collection of precalculated scores,
find the minimum score from it,
and infer the position of the minimizing item from the position of the minimum score.
This is my reading of their suggestion:
template<class InputIt, class Scoring>
auto argmin(InputIt first, InputIt last, Scoring scoring)
{
using score_type = typename std::result_of_t<Scoring(typename std::iterator_traits<InputIt>::value_type)>;
std::vector<score_type> scores(std::distance(first, last));
std::transform(first, last, begin(scores), scoring);
const auto scoremin = std::min_element(begin(scores), end(scores));
return first + std::distance(begin(scores), scoremin);
}
With a live demo.

Keep track of the timer repeatedly in C++

I've a library which accepts a vector of strings as argument & pushes it to a vector of vector of strings (2D vector).
I've a condition that "right from the time 1st row is pushed to the 2D vector the timer should start & for every "X" th milisecond I want to append some special string "FLAG_BORDER" at the end of the vector of string that i receive as argument to library.
LIBRARY CODE
using namespace std;
vector<vector<sring>> vecOfVecStrs;
MyLibraryFunction(vector<string> vecstr)
{
if(TimerLimitNOTHit()) // THIS FUNCTION TimerLimitNOTHit() checks if the "X"
// th milisecond is reached or not.
{
vecOfVecStrs.push_back(vecstr); // If "X"th ms time poeriod is NOT
// yet reached
}
else
{
vecstr.push_back("FLAG_BORDER");
vecOfVecStrs.push_back(vecstr);
}
APPLICATION CODE CALLING LIBRARY FUNCTION::
int main()
{
do_something();
vector<string> vecStr;
while(vecStr = Get_VecTor_of_strings())
{
MyLibraryFunction(vecStr);
}
How do I implement the function TimerLimitNOTHit() here which should
keep track of the timer "X" milisec across function calls to
"MyLibraryFunction()" in C++ ?
EDITED: SEEMS LIKE FOUND AN ANSWER. wILL IT WORK ?
int timeX = 30; //s
bool keepTrack = false;
int ci = 0;
std::vector<int> timeArray;
std::mutex m;
bool keepTracking()
{
std::unique_lock<std::mutex> lock(m);
return keepTrack;
}
void GlobalTimer()
{
while (keepTracking())
{
std::this_thread::sleep_for(std::chrono::seconds(timeX));
timeArray[ci] = 1;
}
}
std::thread t1(GlobalTimer);
Here is some inspiration, where the function TimerLimitNOTHit returns true if more than X ms has passed since it last returned true.
int X = ... // #0
bool TimerLimitNOTHit()
{
static auto start = std::chrono::steady_clock::now(); // #1
auto now = std::chrono::steady_clock::now();
if (now - start > std::chrono::milliseconds(X))
{
start = now // #2
return true;
}
return false
}
#0 Defining the distance in time between two calls to TimerLimitNOTHit that return true.
#1 Initialization depend on your application logic. In this example the function does not return true on the first call, this can changed by initializing start to zero.
#2 start value for the next iteration, again it depends on your application logic. If you want a more steady true you could do some modulo arithmetic.
Let me know if this is not what you were looking for.
Disclaimer
I don't really like the use of static variables, but without a "status" parameter to the function I don't see how it can be avoided.
Furthermore, the use of the global X variable does not fall to my likening neither. The variable X could be changed to a template variable, this better show intent and makes it a compile time constant.

Combining arrays/lists in an specific fashion

I'm trying to find a sensible algorithm to combine multiple lists/vectors/arrays as defined below.
Each element contains a float declaring the start of its range of validity and a constant that is used over this range. Where ranges from different lists overlap their constants need to be added to produce one global list.
I've done an attempt at an illustration below to try and give a good idea of what I mean:
First List:
0.5---------------2------------3.2--------4
a1 a2 a3
Second List:
1----------2----------3---------------4.5
b1 b2 b3
Desired Output:
0.5----1----------2----------3-3.2--------4--4.5
a1 a1+b1 a2+b2 ^ a3+b3 b3
b3+a2
I can't think of a sensible way of going about this in the case of n lists; Just 2 is quite easy to brute force.
Any hints or ideas would be welcome. Each list is represented as a C++ std::vector (so feel free to use standard algorithms) and are sorted by start of range value.
Cheers!
Edit: Thanks for the advice, I've come up with a naive implementation, not sure why I couldn't get here on my own first. To my mind the obvious improvement would be to store an iterator for each vector since they're already sorted and not have to re-traverse each vector for each point. Given that most vectors will contain less than 100 elements, but there may be many vectors this may or may not be worthwhile. I'd have to profile to see.
Any thoughts on this?
#include <vector>
#include <iostream>
struct DataType
{
double intervalStart;
int data;
// More data here, the data is not just a single int, but that
// works for our demonstration
};
int main(void)
{
// The final "data" of each vector is meaningless as it refers to
// the coming range which won't be used as this is only for
// bounded ranges
std::vector<std::vector<DataType> > input = {{{0.5, 1}, {2.0, 3}, {3.2, 3}, {4.0, 4}},
{{1.0, 5}, {2.0, 6}, {3.0, 7}, {4.5, 8}},
{{-34.7895, 15}, {-6.0, -2}, {1.867, 5}, {340, 7}}};
// Setup output vector
std::vector<DataType> output;
std::size_t inputSize = 0;
for (const auto& internalVec : input)
inputSize += internalVec.size();
output.reserve(inputSize);
// Fill output vector
for (const auto& internalVec : input)
std::copy(internalVec.begin(), internalVec.end(), std::back_inserter(output));
// Sort output vector by intervalStartPoints
std::sort(output.begin(), output.end(),
[](const DataType& data1, const DataType& data2)
{
return data1.intervalStart < data2.intervalStart;
});
// Remove DataTypes with same intervalStart - each interval can only start once
output.erase(std::unique(output.begin(), output.end(),
[](const DataType& dt1, const DataType& dt2)
{
return dt1.intervalStart == dt2.intervalStart;
}), output.end());
// Output now contains all the right intersections, just not with the right data
// Lambda to find the associated data value associated with an
// intervsalStart value in a vector
auto FindDataValue = [&](const std::vector<DataType> v, double startValue)
{
auto iter = std::find_if(v.begin(), v.end(), [startValue](const DataType& data)
{
return data.intervalStart > startValue;
});
if (iter == v.begin() || iter == v.end())
{
return 0;
}
return (iter-1)->data;
};
// For each interval in the output traverse the input and sum the
// data constants
for (auto& val : output)
{
int sectionData = 0;
for (const auto& iv : input)
sectionData += FindDataValue(iv, val.intervalStart);
val.data = sectionData;
}
for (const auto& i : output)
std::cout << "loc: " << i.intervalStart << " data: " << i.data << std::endl;
return 0;
}
Edit2: #Stas's code is a very good way to approach this problem. I've just tested it on all the edge cases I could think of.
Here's my merge_intervals implementation in case anyone is interested. The only slight change I've had to make to the snippets Stas provided is:
for (auto& v : input)
v.back().data = 0;
Before combining the vectors as suggested. Thanks!
template<class It1, class It2, class OutputIt>
OutputIt merge_intervals(It1 first1, It1 last1,
It2 first2, It2 last2,
OutputIt destBegin)
{
const auto begin1 = first1;
const auto begin2 = first2;
auto CombineData = [](const DataType& d1, const DataType& d2)
{
return DataType{d1.intervalStart, (d1.data+d2.data)};
};
for (; first1 != last1; ++destBegin)
{
if (first2 == last2)
{
return std::copy(first1, last1, destBegin);
}
if (first1->intervalStart == first2->intervalStart)
{
*destBegin = CombineData(*first1, *first2);
++first1; ++first2;
}
else if (first1->intervalStart < first2->intervalStart)
{
if (first2 > begin2)
*destBegin = CombineData(*first1, *(first2-1));
else
*destBegin = *first1;
++first1;
}
else
{
if (first1 > begin1)
*destBegin = CombineData(*first2, *(first1-1));
else
*destBegin = *first2;
++first2;
}
}
return std::copy(first2, last2, destBegin);
}
Unfortunately, your algorithm is inherently slow. It doesn't make sense to profile or apply some C++ specific tweaks, it won't help. It will never stop calculation on pretty small sets like merging 1000 lists of 10000 elements each.
Let's try to evaluate time complexity of your algo. For the sake of simplicity, let's merge only lists of the same length.
L - length of a list
N - number of lists to be merged
T = L * N - length of a whole concatenated list
Complexity of your algorithm steps:
create output vector - O(T)
sort output vector - O(T*log(T))
filter output vector - O(T)
fix data in output vector - O(T*T)
See, the last step defines the whole algorithm complexity: O(T*T) = O(L^2*N^2). It is not acceptable for practical application. See, to merge 1000 lists of 10000 elements each, the algorithm should run 10^14 cycles.
Actually, the task is pretty complex, so do not try to solve it in one step. Divide and conquer!
Write an algorithm that merges two lists into one
Use it to merge a list of lists
Merging two lists into one
This is relatively easy to implement (but be careful with corner cases). The algorithm should have linear time complexity: O(2*L). Take a look at how std::merge is implemented. You just need to write your custom variant of std::merge, let's call it merge_intervals.
Applying a merge algorithm to a list of lists
This is a little bit tricky, but again, divide and conquer! The idea is to do recursive merge: split a list of lists on two halves and merge them.
template<class It, class Combine>
auto merge_n(It first, It last, Combine comb)
-> typename std::remove_reference<decltype(*first)>::type
{
if (first == last)
throw std::invalid_argument("Empty range");
auto count = std::distance(first, last);
if (count == 1)
return *first;
auto it = first;
std::advance(it, count / 2);
auto left = merge_n(first, it, comb);
auto right = merge_n(it, last, comb);
return comb(left, right);
}
Usage:
auto combine = [](const std::vector<DataType>& a, const std::vector<DataType>& b)
{
std::vector<DataType> result;
merge_intervals(a.begin(), a.end(), b.begin(), b.end(),
std::back_inserter(result));
return result;
};
auto output = merge_n(input.begin(), input.end(), combine);
The nice property of such recursive approach is a time complexity: it is O(L*N*log(N)) for the whole algorithm. So, to merge 1000 lists of 10000 elements each, the algorithm should run 10000 * 1000 * 9.966 = 99,660,000 cycles. It is 1,000,000 times faster than original algorithm.
Moreover, such algorithm is inherently parallelizable. It is not a big deal to write parallel version of merge_n and run it on thread pool.
I know I'm a bit late to the party, but when I started writing this you hadn't a suitable answer yet, and my solution should have a relatively good time complexity, so here you go:
I think the most straightforward way to approach this is to see each of your sorted lists as a stream of events: At a given time, the value (of that stream) changes to a new value:
template<typename T>
struct Point {
using value_type = T;
float time;
T value;
};
You want to superimpose those streams into a single stream (i.e. having their values summed up at any given point). For that you take the earliest event from all streams, and apply its effect on the result stream. Therefore, you need to first "undo" the effect that the previous value from that stream made on the result stream, and then add the new value to the current value of the result stream.
To be able to do that, you need to remember for each stream the last value, the next value (and when the stream is empty):
std::vector<std::tuple<Value, StreamIterator, StreamIterator>> streams;
The first element of the tuple is the last effect of that stream onto the result stream, the second is an iterator pointing to the streams next event, and the last is the end iterator of that stream:
transform(from, to, inserter(streams, begin(streams)),
[] (auto & stream) {
return make_tuple(static_cast<Value>(0), begin(stream), end(stream));
});
To be able to always get the earliest event of all the streams, it helps to keep the (information about the) streams in a (min) heap, where the top element is the stream with the next (earliest) event. That's the purpose of the following comparator:
auto heap_compare = [] (auto const & lhs, auto const & rhs) {
bool less = (*get<1>(lhs)).time < (*get<1>(rhs)).time;
return (not less);
};
Then, as long as there are still some events (i.e. some stream that is not empty), first (re)build the heap, take the top element and apply its next event to the result stream, and then remove that element from the stream. Finally, if the stream is now empty, remove it.
// The current value of the result stream.
Value current = 0;
while (streams.size() > 0) {
// Reorder the stream information to get the one with the earliest next
// value into top ...
make_heap(begin(streams), end(streams), heap_compare);
// .. and select it.
auto & earliest = streams[0];
// New value is the current one, minus the previous effect of the selected
// stream plus the new value from the selected stream
current = current - get<0>(earliest) + (*get<1>(earliest)).value;
// Store the new time point with the new value and the time of the used
// time point from the selected stream
*out++ = Point<Value>{(*get<1>(earliest)).time, current};
// Update the effect of the selected stream
get<0>(earliest) = (*get<1>(earliest)).value;
// Advance selected stream to its next time point
++(get<1>(earliest));
// Remove stream if empty
if (get<1>(earliest) == get<2>(earliest)) {
swap(streams[0], streams[streams.size() - 1u]);
streams.pop_back();
}
}
This will return a stream where there might be multiple points with the same time, but a different value. This occurs when there are multiple "events" at the same time. If you only want the last value, i.e. the value after all these events happened, then one needs to combine them:
merge_point_lists(begin(input), end(input), inserter(merged, begin(merged)));
// returns points with the same time, but with different values. remove these
// duplicates, by first making them REALLY equal, i.e. setting their values
// to the last value ...
for (auto write = begin(merged), read = begin(merged), stop = end(merged);
write != stop;) {
for (++read; (read != stop) and (read->time == write->time); ++read) {
write->value = read->value;
}
for (auto const cached = (write++)->value; write != read; ++write) {
write->value = cached;
}
}
// ... and then removing them.
merged.erase(
unique(begin(merged), end(merged),
[](auto const & lhs, auto const & rhs) {
return (lhs.time == rhs.time);}),
end(merged));
(Live example here)
Concerning the time complexity: This is iterating over all "events", so it depends on the number of events e. The very first make_heap call has to built a complete new heap, this has worst case complexity of 3 * s where s is the number of streams the function has to merge. On subsequent calls, make_heap only has to correct the very first element, this has worst case complexity of log(s'). I write s' because the number of streams (that need to be considered) will decrease to zero. This
gives
3s + (e-1) * log(s')
as complexity. Assuming the worst case, where s' decreases slowly (this happens when the events are evenly distributed across the streams, i.e. all streams have the same number of events:
3s + (e - 1 - s) * log(s) + (sum (log(i)) i = i to s)
Do you really need a data structure as result? I don't think so. Actually you're defining several functions that can be added. The examples you give are encoded using a 'start, value(, implicit end)' tuple. The basic building block is a function that looks up it's value at a certain point:
double valueAt(const vector<edge> &starts, float point) {
auto it = std::adjacent_find(begin(starts), end(starts),
[&](edge e1, edge e2) {
return e1.x <= point && point < e2.x;
});
return it->second;
};
The function value for a point is the sum of the function values for all code-series.
If you really need a list in the end, you can join and sort all edge.x values for all series, and create the list from that.
Unless performance is an issue :)
If you can combine two of these structures, you can combine many.
First, encapsulate your std::vector into a class. Implement what you know as operator+= (and define operator+ in terms of this if you want). With that in place, you can combine as many as you like, just by repeated addition. You could even use std::accumulate to combine a collection of them.

Best practice for declaring variable without initialising it, so auto is unavailable

I want to declare two variables of the same type, and have the compiler figure out the types. However I don't want to initialise one of the variables until later. I don't think I can use auto here, so what's the best option?
std::vector<int> v;
// `start` and `end` should be the same type
auto start = v.begin();
??? end;
// complicated code to assign a value to `end` (i.e. putting
// the code in a function or using ?: is not practical here)
if (...) {
end = ...;
} else {
end = v.end();
}
What's the best way to tell the compiler that end should be the same type as start, but without having to initialise the variable?
auto start = v.begin(), end; // Doesn't work, `end` has to be initialised
decltype(start) end; // Does work, but not sure if it's best practice
Update
A couple of comments have suggested ways that would work in certain situations, so I am clarifying my situation here:
std::vector<int> v;
int amount = 123;
// `start` and `end` should be the same type
auto start = v.begin();
??? end;
// code to assign a value to `end`
if (amount) {
end = start + amount;
amount = 0;
} else {
end = v.end();
}
I believe a lambda function would be trickier here, because amount is being reset to 0 after end is calculated, so in a lambda function that calculates a value for end, amount = 0 would have to come after the return statement. The only option would be to create more local variables, which would incur an (admittedly tiny) performance penalty.
My personal approach would be to call a lambda in-place:
std::vector<int> v;
///////////////////
auto start = v.begin(), end = [&]{
if (...) {
// complicated code to compute a value for `end`
}
else
return v.end();
}();
If automatic return type deduction for the lambda fails for any reason (e.g. there are multiple return statements), just replace [&]{ with [&]() -> decltype(start) {.
Edit:
std::vector<int> v;
int amount = 123;
///////////////////
auto start = v.begin(), end = [&]{
auto ret = v.end();
if (amount) {
ret = start + amount;
amount = 0;
}
return ret;
}();
I think an uninitialized variable here is premature optimization. I would initialize the variable and only consider optimizing if I have evidence that it would have an impact.
auto start = v.begin();
auto end = v.end();
if (amount) {
end = start + amount;
amount = 0;
}
Here is an example using the good old ternary operator (also mentioned by #5gon12eder). In such a simple case it's way the best comprehensible IMHO, and also avoids the problems of uninitialized variables, pointed out by #ildjarn.
auto start = v.begin();
auto end = amount
? start + amount
: v.end();
amount = 0;
auto start = v.begin();
decltype(start) end; // Must have a default constructor though.
Is it not overthinking the problem? Either go for your first intuition:
auto start = v.begin();
decltype(start) end;
Or auto-initialize the end variable with v.end(), and drop the else clause in your demonstration code. It's as simple as that:
auto start = v.begin();
auto end = v.end(); // Or v.begin() depending on your preference, the whole container or nothing by default
// code to assign a value to `end` IF it must be different from v.end()
if (amount) {
end = start + amount;
amount = 0;
}
The cost of initializing or assigning an iterator should be negligible anyway, compared to the maintenance/readability cost.

std::pair as key in map

I have a large dataset of images, taken at specific times where each image capture start_time and stop_time are known and encoded as doubles.
I want to load each consecutive image into my simulation based on the simulation time, ie - check when the current simulation time falls within the start/stop interval.
I want to use a map for this, where the key is a std::pair<double, double> of start & stop time and the value is the full path to the image.
std::map<std::pair<double, double>, std::string> _sequence; // t1, t2 and full path
My question:
How can I search such a map to find if _currentTime is within an interval pair?
Firstly, don't use a map keyed on std::pair<double, double> if searching for inclusion is the main thing you want to do. That's just not an operation that makes sense with that data structure.
But if you insist, the code would look something like this (in C++11):
bool isWithinInterval() const {
for (const auto& pr : _sequence) {
if (_currentTime >= pr.first.first && _currentTime <= pr.first.second) {
return true;
}
}
return false;
}
Pre-C++11, same idea, just slightly different loop syntax. Ideally, we'd use std::find_if, but it's a hassle to express the map's value_type. In C++14 though, no such hassle:
auto it = std::find_if(_sequence.begin(),
_sequence.end(),
[_currentTime](const auto& pr) {
return _currentTime >= pr.first.first && _currentTime <= pr.first.second;
});
return it != _sequence.end();
Or just:
return std::any_of(_sequence.begin(), _sequence.end(),
[_currentTime](const auto& pr) {
return _currentTime >= pr.first.first && _currentTime <= pr.first.second;
});
One approach could be not to use a std::map<std::pair<double, double>, std::string> but rather a std::map<double, std::pair<double, std::string>>: you'd use m.lower_bound(current_time) to find the start of a range of elements where current_time could fit. You'd then walk the iterator until it reaches the end, falls into the relevant range, or is beyond the end time:
auto it = _sequence.lower_bound(current_time);
for (; it != _sequence.end() && current_time <= it->second; ++it) {
if (it.first <= current_time) {
// found a matching element at it
}
}
Using the layout with a std::pair<double, double> a key has the awkward need to come up with a second time. You could use std::make_pair(current_time, current_time), though.
double search = 0.; /* or some other value */
bool found = false;
for ( auto & key_value_pair : _sequence ) {
// key_value_pair.first == map key
// key_value_pair.second == key's associated value
if ( key_value_pair.first.first <= search || search <= key_value_pair.first.second ) {
found = true;
break;
}
}
if ( found ) {
/* it's within an interval pair! */
} else {
/* it's not within an interval pair! */
}
I would recommend also looking into boost::icl
If possible, don't use std::pair as the key. It doesn't really make sense as a key because you end up with a situation where two overlapping ranges map to the same element.
Anyhow, here is how I would implement the solution to such a problem. lower_bound/upper_bound are your friend here. Also, you can avoid the reverse iterator tricks by keying the values on stop time.
#include <map>
#include <stdio.h>
struct ImageStuff
{
double startTime;
double stopTime;
char data[1000];
};
typedef std::map<double, ImageStuff> starttime_map_type;
starttime_map_type starttime_map;
ImageStuff & MakeImage (double start, double stop) {
ImageStuff newImage;
newImage.startTime = start;
newImage.stopTime = stop;
return starttime_map[start] = newImage;
}
starttime_map_type::iterator FindByTime (double time) {
starttime_map_type::reverse_iterator i = starttime_map_type::reverse_iterator(starttime_map.upper_bound(time));
if (i == starttime_map.rend() || time > i->second.stopTime) {
printf ("Didn't find an image for time %f\n", time);
return starttime_map.end();
}
else {
printf ("Found an image for time %f\n", time);
return i.base();
}
return starttime_map.end();
}
int main (void)
{
MakeImage (4.5, 6.5);
MakeImage (8.0, 12);
MakeImage (1, 1.2);
auto i = FindByTime(3);
i = FindByTime(4.5);
i = FindByTime(9);
i = FindByTime(15);
return 0;
}