Related
Does C++17 (or earlier but not c++20) allow this?
I need a type_traited conditional class, like a bitset with an internal 32 or 64 unsigned integer storage, depending if the template argument N is lesser then 32 or greater (please forget about more than 64 bits).
But the constraint is to finally implement two and only two classes for all possible cases. Next source code defines the problem using static and running time asserts:
Coliru link: http://coliru.stacked-crooked.com/a/d53a5b00bd828fb5
#include <cassert>
#include <iostream>
#include <type_traits>
struct bitset32
{
bitset32() : bits(0) { }
bitset32(int _bits) : bits(_bits) { }
const int bits;
uint32_t value;
};
struct bitset64
{
bitset64() : bits(0) { }
bitset64(int _bits) : bits(_bits) { }
const int bits;
uint64_t value;
};
template <int N>
using bitset = std::conditional_t<(N<=32), bitset32, bitset64>;
int main ()
{
static_assert(std::is_same<bitset<1>, bitset<2>>::value);
static_assert(std::is_same<bitset<33>, bitset<34>>::value);
static_assert(!std::is_same<bitset<1>, bitset<33>>::value);
bitset<1> var1;
bitset<15> var2;
bitset<32> var3;
bitset<64> var4;
assert(var1.bits == 1);
assert(var2.bits == 15);
assert(var3.bits == 32);
assert(var4.bits == 64);
}
Any solution is welcomend even if it changes the basic types and uses inheritance or whatever other mechanism necessary, but please, do not offer using a template function returning an object in the style of template<int N> make_bitset { return Bitset<N>(N); } because it is needed to implement variables using this constructo Bitset<N> variable_name.
A new hypothesis, CTAD based
#include <cstdint>
#include <iostream>
#include <type_traits>
template <std::size_t>
struct BitSet;
template <>
struct BitSet<32u>
{
template <typename ... Ts>
BitSet (Ts...) {}
std::uint32_t value;
};
template <>
struct BitSet<64u>
{
template <typename ... Ts>
BitSet (Ts...) {}
std::uint64_t value;
};
template <typename ... Ts>
BitSet (Ts...) -> BitSet<(32 < sizeof...(Ts) ? 64u : 32u)>;
int main()
{
auto b1 = BitSet{1};
auto b2 = BitSet{2};
auto b3 = BitSet{0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39};
auto b4 = BitSet{0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49};
static_assert( std::is_same_v<decltype(b1), decltype(b2)> );
static_assert( std::is_same_v<decltype(b1), BitSet<32u>> );
static_assert( std::is_same_v<decltype(b3), decltype(b4)> );
static_assert( std::is_same_v<decltype(b3), BitSet<64u>> );
}
You can just make bitset32 and bitset64 templates like
template <std::size_t BitsUsed>
struct bitset32
{
bitset32() : bits(BitsUsed) { }
bitset32(int _bits) : bits(_bits) { }
const int bits;
uint32_t value;
};
template <std::size_t BitsUsed>
struct bitset64
{
bitset64() : bits(BitsUsed) { }
bitset64(int _bits) : bits(_bits) { }
const int bits;
uint64_t value;
};
and now the default constructor will pull in the correct number of bits. Then you just need to modify your alias like
template <int N>
using bitset = std::conditional_t<(N<=32), bitset32<N>, bitset64<N>>;
and you get the behavior you are asking for.
This will not work if you want to store a bitset<5> in the same container as a bitset<32> though since different template instantiations create distinct type.
I have a linux time clock, that sending me the clock with bytes, I have no idea how to get the clock out of it, I do know that it's a type of "time_t" (which is probably the total seconds since 1/1/1970), See below a few examples what I'm receiving from the clock
(I'm not sure if it's sending local or GMT time)
This bytes represents 11/09/2017 16:52(Local time) or 11/09/2017 21:52(GMT)
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,129,66,120,53,34,0,0,0
This bytes represents 11/10/2017 10:27(Local time) or 11/10/2017 15:27(GMT)
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,129,162,111,54,34,0,0,0
This bytes represents 11/13/2017 14:20(Local time) or 11/13/2017 19:20(GMT)
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,129,160,154,58,34,0,0,0
With a little reverse engineering, it's possible to see what the data from your Linux clock is doing.
If you read your lines of data, you can see that the final 7 bytes are an incrementing counter, with the least significant byte in the first position. If you do the maths, the 3 counters give the following totals:
573929538
573992866
574266016
Looking at the differences from the first number to the second and third, you get the following:
63328
336478
... which are the elapsed times between your samples, in seconds.
For some reason, the raw values aren't the time in seconds since the Unix Epoch, but the time in seconds since a day early in September 1999.
Anyway, the following source code shows the basics of how you can convert your data into the local time.
#include <time.h>
int main(int argc, char* argv[])
{
const unsigned long localEpoch = 936316800; // local epoch Sept 1999
std::vector<std::vector<int>> timeData =
{
{ 66, 120, 53, 34, 0, 0, 0 },
{ 162, 111, 54, 34, 0, 0, 0 },
{ 160, 154, 58, 34, 0, 0, 0 }
};
for (auto& data : timeData)
{
// convert to total seconds
unsigned long total = 0;
for (std::vector<int>::iterator it = data.begin(); it != data.end(); ++it)
{
total += (*it) << (std::distance(data.begin(), it) * 8);
}
std::cout << "Seconds: " << total << std::endl;
// add to local epoch and display
time_t raw = total + localEpoch;
struct tm timeinfo;
localtime_s(&timeinfo, &raw);
char fmt[100] = { 0 };
asctime_s(fmt, 100, &timeinfo);
std::cout << fmt << std::endl;
}
return 0;
}
And it gives the following outputs:
Seconds: 573929538
Thu Nov 9 16:52:18 2017
Seconds: 573992866
Fri Nov 10 10:27:46 2017
Seconds: 574266016
Mon Nov 13 14:20:16 2017
Hope this is of some help. (NB I did this in VS, so the time formatting functions are of the Windows variety, but you get the idea.)
(Edit): OK as requested, here's some equivalent C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
public static void Main(string[] args)
{
int[,] timeData = new int[,]
{
{ 66, 120, 53, 34, 0, 0, 0 },
{ 162, 111, 54, 34, 0, 0, 0 },
{ 160, 154, 58, 34, 0, 0, 0 }
};
for (int line = 0; line < timeData.GetLength(0); ++line)
{
ulong total = 0;
for (int i = 0; i < timeData.GetLength(1); ++i)
{
total += (ulong)timeData[line,i] << (8 * i);
}
System.Console.WriteLine("Seconds: " + total.ToString());
DateTime result = FromUnixTime(total + localEpoch);
System.Console.WriteLine(result.ToString());
}
System.Console.ReadKey();
}
public static DateTime FromUnixTime(ulong unixTime)
{
return epoch.AddSeconds(unixTime);
}
private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private static readonly ulong localEpoch = 936316800; // local epoch Sept 1999
}
}
(Thanks to this question for the Epoch conversion.)
WOW!!!, that helped me a lot, Here is the code what I used, Thanks #ASMackay
private DateTime convertTime(byte[] bytes)
{
ulong localEpoch = 936316800; // local epoch Sept 1999
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
ulong total = 0;
for (int i = 0; i < bytes.Count(); ++i)
{
total += (ulong)bytes[i] << (8 * i);
}
return epoch.AddSeconds(total + localEpoch);
}
I have a series of structs:
const struct weapon Dagger = { 1, 40, 5, 20, 30, "Dagger" };
const struct weapon Sword = { 2, 35, 25, 40, 60, "Sword" };
const struct weapon Axe = { 4, 50, 10, 70, 80, "Axe" };
const struct ...
I want to arrange them so I can access each one by integer. I am trying to build a function that takes int x and int y as arguments, and returns the indexed struct's data. For example, if the function takes 2 and 3, respectively, the value 35 from the weapon struct will be returned. Intuitively, I imagined the function body looking something like return weapon[x].y, although this does not work.
I do not know how to do this. The best alternative I can see is to use arrays instead of structs. Should I do this instead?
If you are only after the numeric data, then you could use two arrays: one for the data itself, and one for accessor function pointers:
struct weapon
{
int val;
int hp;
int foo;
// ...
std::string name;
};
const weapon w[] = {
{ 1, 40, 5, 20, 30, "Dagger" },
{ 2, 35, 25, 40, 60, "Sword" },
{ 4, 50, 10, 70, 80, "Axe" },
};
using accessor = int weapon::*;
const accessor acc[] = {
&weapon::val,
&weapon::hp,
&weapon::foo,
// ...
};
Now to look up property j in weapon i (both zero-indexed), you could say:
w[i].*acc[j]
Alternatively, you could perhaps represent your data as an array of pairs:
std::pair<const char*, std::array<int, 5>> w_alt[] = {
{ "Dagger", { 1, 40, 5, 20, 30 } },
{ "Sword", { 2, 35, 25, 40, 60 } },
{ "Axe", { 4, 50, 10, 70, 80 } },
};
Now the ith weapon's anme is w_alt[i].first, and its jth property is w_alt[i].second[j].
If you want a container that has indexed access, you probably want std::vector. Something like:
std::vector<weapon> WeaponVector;
For the first part of your question (selecting weapon based on index), using an array is a simple option, e.g.:
std::array<weapon, 3> const weapons =
{ { 1, 40, 5, 20, 30, "Dagger" }
, { 2, 35, 25, 40, 60, "Sword" }
, { 4, 50, 10, 70, 80, "Axe" }
};
Then, if you want, you can make specific references:
weapon const &Sword = weapons[1];
or make indices which you'll use to access the array:
enum weapon_id { Dagger, Sword, Axe };
// ...
do_something( weapons[Sword] );
Using std::array instead of a C-style array weapon const weapons[] =
has a drawback that you can't deduce the dimension, but it has other benefits that make up for this.
For the second part, you may add a function that looks up the weapon properties by index. For example:
int weapon_lookup_number(weapon const &w, int index)
{
switch(index)
{
case 0: return w.id;
case 1: return w.power;
// etc.
}
}
with another function for looking up the string.
Using template specialization it'd be possible to have a function that looks up the member by index and evaluates to the correct type, however that would only work in the case of the index being known at compile-time.
If your struct becomes:
struct weapon {
int val;
int hp;
int foo;
// ...
std::string name;
};
You could use a std::vector<weapon> Weapons to store the structs, sort it using:
std::sort(Weapons.begin(), Weapons.end(),
[](const Weapons& w1,const Weapons& w2) -> bool { return w1.val > w2.val; }))
and access them using the vector indexes like: weapons[some_num].hp;, where some_num will correspond to the wanted val.
I have an interface where I'm getting a file and the file name is the time stamp when the internal data was valid. I'm processing the files in order and providing them to another API which takes the time as milliseconds since Jan 1st, 1970.
So, I get the file name in as a string in the format of YYYYMMDD_HHmmSS.sss and have to turn this into time. I assume the processing and collection occurred in the same timezone and such and simply have to convert the characters to digits.
uint64_t foo::convertFileNameToTimestamp(const std::string& filename) const
{
const uint64_t yyyy = atoi(filename.substr(0,4).c_str());
const uint64_t MM = atoi(filename.substr(4,2).c_str());
const uint64_t DD = atoi(filename.substr(6,2).c_str());
const uint64_t HH = atoi(filename.substr(9,2).c_str());
const uint64_t MI = atoi(filename.substr(11,2).c_str());
const uint64_t SS = atoi(filename.substr(13,2).c_str());
const uint64_t sss = atoi(filename.substr(16,3).c_str());
// number of milliseconds in a day
const uint64_t DAY = 24 * 60 * 60 * 1000;
int MD = 0;
if (MM == 2) MD = 31; // currently feb, so add all of january's days
else if (MM == 3) MD = 31+28; // ...
else if (MM == 4) MD = 31+28+31;
else if (MM == 5) MD = 31+28+31+30;
else if (MM == 6) MD = 31+28+31+30+31;
else if (MM == 7) MD = 31+28+31+30+31+30;
else if (MM == 8) MD = 31+28+31+30+31+30+31;
else if (MM == 9) MD = 31+28+31+30+31+30+31+31;
else if (MM == 10) MD = 31+28+31+30+31+30+31+31+30;
else if (MM == 11) MD = 31+28+31+30+31+30+31+31+30+31;
else if (MM == 12) MD = 31+28+31+30+31+30+31+31+30+31+30;
// year 2000 wasn't a leap year
uint64_t YLD = ((yyyy-1970) / 4);
if (yyyy > 2000) --YLD;
uint64_t temp = sss;
temp += SS * 1000;
temp += MI * 60 * 1000;
temp += HH * 60 * 60 * 1000;
temp += (DD-1) * DAY;
temp += (MD) * DAY;
temp += (yyyy-1970) * 365 * DAY + YLD*DAY;
return temp;
}
Obviously, reinventing the wheel here. Seems like there should be some sort of function for this. Also.. how do I account for leap seconds? It was annoying enough to deal with leap days. The time stamps are all from 2015 and beyond and always will be, but I don't think I can just blindly add 26 seconds. Eventually we will have 27 or back up to 25. In previous functions I have validated the string to be the right format and the file to exist and all that jazz. I'm running on windows 8.1 compilation for 64 bit using VS 2010.
I've looked at Convert Epoch Time string to Time which suggested ctime(), but it doesn't seem to deal with milliseconds in the constructor or even any of the get methods and it doesn't accept generically formatted string inputs. I'm assuming I've got to call some time classes CTOR which will accept the file name string and then call some accessor on it to get the millisecond time since 1970 including leap seconds and such.
I am not using boost and don't have access/permission to use it.
Here is an answer that will work on any platform that supports C++11 or C++14. It builds off of the std::chrono library that was introduced in C++11. It also uses a free, open source, cross platform library to simplify the arithmetic (MIT license which is usually considered lawyer-friendly).
If you don't need to take leap seconds into account, you can use this date library, and it will look like this:
#include <string>
#include "date.h"
using time_stamp = std::chrono::time_point<std::chrono::system_clock,
std::chrono::milliseconds>;
time_stamp
convertFileNameToTimestamp(const std::string& filename)
{
using namespace std::chrono;
using namespace date;
const uint64_t yyyy = atoi(filename.substr(0,4).c_str());
const uint64_t MM = atoi(filename.substr(4,2).c_str());
const uint64_t DD = atoi(filename.substr(6,2).c_str());
const uint64_t HH = atoi(filename.substr(9,2).c_str());
const uint64_t MI = atoi(filename.substr(11,2).c_str());
const uint64_t SS = atoi(filename.substr(13,2).c_str());
const uint64_t sss = atoi(filename.substr(16,3).c_str());
return sys_days{year(yyyy)/MM/DD}
+ hours{HH} + minutes{MI} + seconds{SS} + milliseconds{sss};
}
After parsing the numbers out of the filename, it is very simple to create a type-safe std::chrono::time_point which simply holds an integral number of milliseconds since 1970-01-01 (as an int64_t).
If you want to take leap seconds into account, you need this higher-level library which is a complete parser of the IANA timezone database. You will also need to keep an updated copy of the IANA timezone database downloaded for my timezone/leap-second library to parse. But once set up, the source code for your converter is very similar to that above, and nearly as simple:
#include <string>
#include "tz.h"
using time_stamp_ls = std::chrono::time_point<date::utc_clock,
std::chrono::milliseconds>;
time_stamp_ls
convertFileNameToTimestamp_ls(const std::string& filename)
{
using namespace std::chrono;
using namespace date;
const uint64_t yyyy = atoi(filename.substr(0,4).c_str());
const uint64_t MM = atoi(filename.substr(4,2).c_str());
const uint64_t DD = atoi(filename.substr(6,2).c_str());
const uint64_t HH = atoi(filename.substr(9,2).c_str());
const uint64_t MI = atoi(filename.substr(11,2).c_str());
const uint64_t SS = atoi(filename.substr(13,2).c_str());
const uint64_t sss = atoi(filename.substr(16,3).c_str());
return utc_clock::sys_to_utc(sys_days{year(yyyy)/MM/DD}
+ hours{HH} + minutes{MI} + seconds{SS} + milliseconds{sss});
}
Both of these functions can be exercised with the following HelloWorld:
#include <iostream>
int
main()
{
std::string filename = "20150830_002120.123";
std::cout << convertFileNameToTimestamp (filename).time_since_epoch().count() << '\n';
std::cout << convertFileNameToTimestamp_ls(filename).time_since_epoch().count() << '\n';
}
which outputs:
1440894080123
1440894106123
Note that these timestamps are exactly 26,000ms apart.
Update
The "tz.h" header now includes a parse function which makes writing these functions much easier:
date::sys_time<std::chrono::milliseconds>
convertFileNameToTimestamp(const std::string& filename)
{
using namespace std::chrono;
using namespace date;
std::istringstream in{filename};
sys_time<milliseconds> tp;
parse(in, "%Y%m%d_%H%M%S", tp);
return tp;
}
date::utc_time<std::chrono::milliseconds>
convertFileNameToTimestamp_ls(const std::string& filename)
{
return date::to_utc_time(convertFileNameToTimestamp(filename));
}
You can use this piece of code (you don't have to worry about leap years and all the related stuff).
#Edit1: Modified the code to take leap seconds into account; also restructured it into a class:
Foo.h:
#ifndef __FOO__H__
#define __FOO__H__
#include <string>
#include <Windows.h>
#include <stdint.h>
class CFoo {
private:
const static int kLeapSecsDim = 26;
static uint64_t msecsBetweenEpochs;
static SYSTEMTIME leapSecs[kLeapSecsDim];
ULARGE_INTEGER leapSecsUi[kLeapSecsDim];
int CFoo::getLeapSeconds(ULARGE_INTEGER ui) const;
public:
CFoo();
~CFoo() {};
uint64_t toEpoch(const std::string& filename) const;
};
#endif //__FOO__H__
Foo.cpp:
#include "Foo.h"
uint64_t CFoo::msecsBetweenEpochs = 11644473600000; /* Milliseconds between 1.1.1601 and 1.1.1970 */
SYSTEMTIME CFoo::leapSecs[CFoo::kLeapSecsDim] =
{{1972, 06, 0, 30, 23, 59, 59, 999},
{1972, 12, 0, 31, 23, 59, 59, 999},
{1973, 12, 0, 31, 23, 59, 59, 999},
{1974, 12, 0, 31, 23, 59, 59, 999},
{1975, 12, 0, 31, 23, 59, 59, 999},
{1976, 12, 0, 31, 23, 59, 59, 999},
{1977, 12, 0, 31, 23, 59, 59, 999},
{1978, 12, 0, 31, 23, 59, 59, 999},
{1979, 12, 0, 31, 23, 59, 59, 999},
{1981, 06, 0, 30, 23, 59, 59, 999},
{1982, 06, 0, 30, 23, 59, 59, 999},
{1983, 06, 0, 30, 23, 59, 59, 999},
{1985, 06, 0, 30, 23, 59, 59, 999},
{1987, 12, 0, 31, 23, 59, 59, 999},
{1989, 12, 0, 31, 23, 59, 59, 999},
{1990, 12, 0, 31, 23, 59, 59, 999},
{1992, 06, 0, 30, 23, 59, 59, 999},
{1993, 06, 0, 30, 23, 59, 59, 999},
{1994, 06, 0, 30, 23, 59, 59, 999},
{1995, 12, 0, 31, 23, 59, 59, 999},
{1997, 06, 0, 30, 23, 59, 59, 999},
{1998, 12, 0, 31, 23, 59, 59, 999},
{2005, 12, 0, 31, 23, 59, 59, 999},
{2008, 12, 0, 31, 23, 59, 59, 999},
{2012, 06, 0, 30, 23, 59, 59, 999},
{2015, 06, 0, 30, 23, 59, 59, 999},
};
int CFoo::getLeapSeconds(ULARGE_INTEGER ui) const {
int ret = 0;
for (int i = 0; i < kLeapSecsDim; i++) {
if (ui.QuadPart <= this->leapSecsUi[i].QuadPart)
break;
ret++;
}
return ret;
}
CFoo::CFoo() {
FILETIME ft;
BOOL res;
for (int i = 0; i < this->kLeapSecsDim; i++) {
res = SystemTimeToFileTime(&(this->leapSecs[i]), &ft);
if (res == FALSE)
throw std::exception("SystemTimeToFileTime error", GetLastError());
this->leapSecsUi[i].LowPart = ft.dwLowDateTime;
this->leapSecsUi[i].HighPart = ft.dwHighDateTime;
}
}
uint64_t CFoo::toEpoch(const std::string& filename) const {
SYSTEMTIME st;
FILETIME ft;
ULARGE_INTEGER ui;
st.wYear = atoi(filename.substr(0, 4).c_str());
st.wMonth = atoi(filename.substr(4, 2).c_str());
st.wDay = atoi(filename.substr(6, 2).c_str());
st.wHour = atoi(filename.substr(9, 2).c_str());
st.wMinute = atoi(filename.substr(11, 2).c_str());
st.wSecond = atoi(filename.substr(13, 2).c_str());
st.wMilliseconds = atoi(filename.substr(16, 3).c_str());
BOOL result = SystemTimeToFileTime(&st, &ft);
if (result == FALSE)
throw std::exception("SystemTimeToFileTime error", GetLastError());
ui.HighPart = ft.dwHighDateTime;
ui.LowPart = ft.dwLowDateTime;
//printf("%016I64X - %I64u\n", ui.QuadPart, ui.QuadPart);
//printf("%016I64X - %I64u\n", ui.QuadPart/10000, ui.QuadPart/10000);
return (ui.QuadPart / 10000) - this->msecsBetweenEpochs + this->getLeapSeconds(ui) * 1000;
}
Notes:
For invalid dates/times SystemTimeToFileTime will fail
The constant CFoo::msecsBetweenEpochs I think it can be found on Google; I took it from Python(2.7.10)'s posixmodule.c (actually there's the seconds number; I only had to multiply it by 1000)
Your implementation yields results that are not very accurate (I used http://www.epochconverter.com for reference).
According to SystemTimeToFileTime, the timestamp is in UTC.
I'm writing a software that stores GenericOrder (containing a quantity, a price, a way and a timestamp) as shared_ptr.
I've read Boost documentation and succeed to define a MultiIndexOrderContainer using three indexes: way, timestamp and price.
But I don't find a way to iterate on specific orders using multiple indexes at the same time.
#include <memory>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
using namespace ::boost;
using namespace ::boost::multi_index;
enum class Way
{
UNDEFINED,
BUY,
SELL
};
template <typename QuantityType, typename PriceType>
struct GenericOrder
{
explicit GenericOrder(const Way way, const QuantityType& quantity, const PriceType& price, const long long& timestamp)
: way_(way), quantity_(quantity), price_(price), timestamp_(timestamp)
{
}
~GenericOrder() = default;
GenericOrder(const GenericOrder&) = delete;
GenericOrder& operator=(const GenericOrder&) = delete;
Way way_;
QuantityType quantity_;
PriceType price_;
long long timestamp_ = -1;
};
// Aliases
using QuantityType = int;
using PriceType = int;
using OrderType = GenericOrder<QuantityType, PriceType>;
using PointerType = std::shared_ptr<OrderType>;
struct way {};
struct timestamp {};
struct price {};
using MultiIndexOrderContainer = multi_index_container<PointerType,
indexed_by<
ordered_non_unique<tag<way>, member<OrderType, decltype(OrderType::way_), &OrderType::way_ >>,
ordered_non_unique<tag<timestamp>, member<OrderType, decltype(OrderType::timestamp_), &OrderType::timestamp_ >>,
ordered_non_unique<tag<price>, member<OrderType, decltype(OrderType::price_), &OrderType::price_>>
>
>;
int main()
{
MultiIndexOrderContainer c;
// Inserting some orders
c.insert(std::make_shared<OrderType>(Way::BUY, 10, 15, 0));
c.insert(std::make_shared<OrderType>(Way::BUY, 10, 14, 1));
c.insert(std::make_shared<OrderType>(Way::BUY, 10, 13, 2));
c.insert(std::make_shared<OrderType>(Way::SELL, 10, 16, 3));
c.insert(std::make_shared<OrderType>(Way::SELL, 10, 17, 4));
c.insert(std::make_shared<OrderType>(Way::SELL, 10, 18, 5));
return 0;
}
I would like to iterate on:
On buying orders with a specific price sorted by timestamp
Cheapest order price from selling orders
Costliest order price from buying orders
How can I achieve that ?
Boost.MultiIndex does not provide any special mechanism for mixing the orders induced by different indices: with the structure you propose you're basically down to querying for the first parameter and then doing a linear scan on the returned range.
On the other hand, if your queries are always of the form (attr1, attr2, attr3) you can speed them up by using composite keys. In your particular case, you can have the three queries you're after with a composite key on (way_,price_,timestamp_)
Live On Coliru
#include <memory>
#include <iostream>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
using namespace ::boost;
using namespace ::boost::multi_index;
enum class Way
{
UNDEFINED,
BUY,
SELL
};
template <typename QuantityType, typename PriceType>
struct GenericOrder
{
explicit GenericOrder(const Way way, const QuantityType& quantity, const PriceType& price, const long long& timestamp)
: way_(way), quantity_(quantity), price_(price), timestamp_(timestamp)
{
}
~GenericOrder() = default;
GenericOrder(const GenericOrder&) = delete;
GenericOrder& operator=(const GenericOrder&) = delete;
Way way_;
QuantityType quantity_;
PriceType price_;
long long timestamp_ = -1;
};
template <typename QuantityType, typename PriceType>
std::ostream& operator<<(std::ostream& os,const GenericOrder<QuantityType,PriceType>& o)
{
switch(o.way_){
case Way::UNDEFINED: os<<"UNDEFINED, ";break;
case Way::BUY: os<<"BUY, ";break;
case Way::SELL: os<<"SELL, ";break;
}
return os<<o.price_<<", "<<o.timestamp_<<"\n";
}
// Aliases
using QuantityType = int;
using PriceType = int;
using OrderType = GenericOrder<QuantityType, PriceType>;
using PointerType = std::shared_ptr<OrderType>;
struct way {};
struct timestamp {};
struct price {};
using MultiIndexOrderContainer = multi_index_container<PointerType,
indexed_by<
ordered_non_unique<
composite_key<
OrderType,
member<OrderType, decltype(OrderType::way_), &OrderType::way_ >,
member<OrderType, decltype(OrderType::price_), &OrderType::price_>,
member<OrderType, decltype(OrderType::timestamp_), &OrderType::timestamp_ >
>
>
>
>;
int main()
{
MultiIndexOrderContainer c;
// Inserting some orders
c.insert(std::make_shared<OrderType>(Way::BUY, 10, 15, 0));
c.insert(std::make_shared<OrderType>(Way::BUY, 10, 14, 1));
c.insert(std::make_shared<OrderType>(Way::BUY, 10, 13, 2));
c.insert(std::make_shared<OrderType>(Way::BUY, 10, 15, 1));
c.insert(std::make_shared<OrderType>(Way::SELL, 10, 16, 3));
c.insert(std::make_shared<OrderType>(Way::SELL, 10, 17, 4));
c.insert(std::make_shared<OrderType>(Way::SELL, 10, 18, 5));
std::cout<<"Buying orders for 15, sorted by timestamp\n";
auto p=c.equal_range(std::make_tuple(Way::BUY,15));
while(p.first!=p.second)std::cout<<**p.first++;
std::cout<<"Cheapest selling order\n";
std::cout<<**c.lower_bound(Way::SELL);
std::cout<<"Costliest buying order\n";
std::cout<<**--c.upper_bound(Way::BUY);
return 0;
}