Related
A typedef of an instantiation of std::initializer_list does not behave the same as the instantiation itself. In the following code (compiled with gcc 4.4.6) a variable declared using the instantiation directly requires single parentheses for initialization, while a variable declared using the typedef requires double parentheses. (Oddly, the compiler gives an 'unused variable' warning for the former but not for the latter.) Also, the argument type can apparently be deduced for a function parameter declared with the instantiation, but not for a function parameter declared with the typedef.
struct S { int a; int b; };
template class std::initializer_list< S >; // same results with or without this
typedef std::initializer_list< S > SL;
void fs1( std::initializer_list< S > data ) {}
void fs2( SL data ) {}
int main()
{
std::initializer_list< S > s11{ { 11, 22 }, { 33, 44 } }; // compiles w/ single parentheses
// std::initializer_list< S > s12{{ { 11, 22 }, { 33, 44 } }}; // ERROR 1
// SL s21{ { 55, 66 }, { 77, 88 } }; // ERROR 2
SL s22{{ { 55, 66 }, { 77, 88 } }}; // compiles w/ double parentheses
fs1( { { 11, 22 }, { 33, 44 } } ); // ok w/ no arg. type
// fs2( { { 55, 66 }, { 77, 88 } } ); // ERROR 3
// fs2( {{ { 55, 66 }, { 77, 88 } }} ); // ERROR 4
// fs2( SL{ { 55, 66 }, { 77, 88 } } ); // ERROR 2
fs2( SL{{ { 55, 66 }, { 77, 88 } }} ); // requires arg. type & double par. to compile
}
// ERROR 1: could not convert '{{{11, 22}, {33, 44}}}' to 'std::initializer_list<S>'
// ERROR 2: no matching function for call to 'std::initializer_list<S>::initializer_list(<brace-enclosed initializer list>)'
// ERROR 3: could not convert '{{55, 66}, {77, 88}}' to 'SL'
// ERROR 4: could not convert '{{{55, 66}, {77, 88}}}' to 'SL'
Why does the typedef'ed instantiation behave differently from the non-typedef'ed version? Is there a different syntax that would enable type deduction without specifying the argument type for the typedef'ed version?
BTW, I don't know if a type alias would exhibit the same difference in behavior, but it's not an option since I'm stuck with gcc 4.4.6 for now.
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 trying to create a PPU interpreter through embedded opcode arrays to execute a handler to interpret the instruction. An instruction can be one of several forms (I-Form, B-Form, D-Form, X-Form, etc.) but I have a small issue with X-Form for primary opcode 31. I'm using a template class InterpretArray which auto-assigns handlers in an array. This class can also be used as a sub-array of handlers which can be invoked from an handler. Compiler VS2013 gives me that fatal error:
1>main.cpp(196): fatal error C1202: recursive type or function dependency context too complex
template < size_t xo_rc >
struct Interpreter < X_Form_31_XORc < xo_rc > >
It sounds like compiler does not like X_Form_31_XORc, any idea why? how can I avoid it?
Here the source that can be compiled fine if X_FORM is undefined:
#include <iostream>
#define B_FORM
//#define X_FORM
using namespace std;
// Dummy stuff
typedef unsigned int u32;
typedef signed int s32;
union Instruction
{
#define FIELD(from, to, type) struct{ u32:(32-to-1); type:(to-from+1); u32:from; }
u32 instruction;
// Opcode fields
FIELD(0, 5, u32 opcode); // Primary opcode
FIELD(26, 31, u32 op4); // Extended opcode of 6-bits (up to 0x3F)
FIELD(21, 31, u32 op4_); // Extended opcode of 11-bits (up to 0x7FF)
FIELD(21, 30, u32 op19); // Extended opcode of 10-bits (up to 0x3FF)
FIELD(27, 29, u32 op30); // Extended opcode of 3-bits (up to 0x7)
FIELD(21, 30, u32 op31); // Extended opcode of 10-bits (up to 0x3FF)
FIELD(30, 31, u32 op58); // Extended opcode of 2-bits (up to 0x3)
FIELD(26, 30, u32 op59); // Extended opcode of 5-bits (up to 0x1F)
FIELD(30, 31, u32 op62); // Extended opcode of 2-bits (up to 0x3)
FIELD(26, 30, u32 op63); // Extended opcode of 5-bits (up to 0x1F)
FIELD(21, 30, u32 op63_); // Extended opcode of 10-bits (up to 0x3FF)
// Instruction fields
FIELD(30, 30, u32 aa); // Bit/Flags: Absolute address bit
FIELD(31, 31, u32 lk); // Bit/Flags: Link bit: Update the link register (LR)
FIELD(21, 21, u32 oe); // Bit/Flags: OE bit: Enable enable setting OV and SO in the XER
FIELD(31, 31, u32 rc); // Bit/Flags: Record bit: Update the condition register (CR)
FIELD(6, 6, u32 l6); // Bit/Flags: ?
FIELD(10, 10, u32 l10); // Bit/Flags: ?
FIELD(11, 11, u32 l11); // Bit/Flags: ?
FIELD(9, 10, u32 l9_10); // Bit/Flags: ?
FIELD(6, 10, u32 bo); // Branching: Options for the branch conditional instructions
FIELD(11, 15, u32 bi); // Branching: CR bit to trigger branch conditional instructions
FIELD(16, 29, s32 bd); // Branching: Immediate 14-bit signed integer for branch displacement
FIELD(19, 20, u32 bh); // ?
FIELD(11, 13, u32 bfa); // ?
FIELD(6, 8, u32 crfd); // CR fields: Destination CR or FPSCR field
FIELD(11, 13, u32 crfs); // CR fields: Source CR or FPSCR field
FIELD(6, 10, u32 crbd); // CR fields: Destination bit in the CR or FPSCR
FIELD(11, 15, u32 crba); // CR fields: Source bit in the CR
FIELD(16, 20, u32 crbb); // CR fields: Source bit in the CR
FIELD(12, 19, u32 crm); // Identify the CR fields that are to be updated by the mtcrf instruction
FIELD(16, 31, s32 d); // Immediate 16-bit signed integer
FIELD(16, 27, u32 dq); // ?
FIELD(16, 29, s32 ds); // ?
FIELD(7, 14, u32 fm); // ?
FIELD(6, 10, u32 frd); // FPR: Destination
FIELD(6, 10, u32 frs); // FPR: Source
FIELD(11, 15, u32 fra); // FPR: Source
FIELD(16, 20, u32 frb); // FPR: Source
FIELD(21, 25, u32 frc); // FPR: Source
FIELD(16, 19, u32 imm); // Immediate for to place in FPSCR
FIELD(6, 29, s32 li); // Branching:
FIELD(6, 29, s32 ll); // Branching:
FIELD(21, 25, u32 mb); // First '1' bit of a 64-bit mask in rotate instructions
FIELD(26, 26, u32 mb_); // First '1' bit of a 64-bit mask in rotate instructions: Split field
FIELD(26, 30, u32 me); // Last '1' bit of a 64-bit mask in rotate instructions
FIELD(21, 25, u32 me_); // Last '1' bit of a 64-bit mask in rotate instructions: Split field
FIELD(26, 26, u32 me__); // Last '1' bit of a 64-bit mask in rotate instructions: Split field
FIELD(16, 20, u32 nb); // Number of bytes to move in an immediate string load or store
FIELD(6, 10, u32 rd); // GPR: Destination
FIELD(6, 10, u32 rs); // GPR: Source
FIELD(11, 15, u32 ra); // GPR: Source
FIELD(16, 20, u32 rb); // GPR: Source
FIELD(16, 20, u32 sh); // Shift amount
FIELD(30, 30, u32 sh_); // Shift amount: Split field
FIELD(11, 20, u32 spr); // Special-purpose register
FIELD(9, 10, u32 strm); // ?
FIELD(20, 26, u32 lev); // ?
FIELD(16, 31, s32 simm); // Immediate 16-bit signed integer
FIELD(16, 31, u32 uimm); // Immediate 16-bit unsigned integer
FIELD(9, 10, u32 th); // Data stream variant of the dcbt instruction
FIELD(6, 10, u32 to); // Trap conditions
FIELD(6, 10, u32 vd); // Vector/SIMD: Destination vector register
FIELD(6, 10, u32 vs); // Vector/SIMD: Source vector register
FIELD(11, 15, u32 va); // Vector/SIMD: Source vector register
FIELD(16, 20, u32 vb); // Vector/SIMD: Source vector register
FIELD(21, 25, u32 vc); // Vector/SIMD: Source vector register
FIELD(22, 25, u32 vshb); // Vector/SIMD: Specifies a shift amount in bytes
FIELD(11, 15, s32 vsimm); // Vector/SIMD: Immediate 5-bit signed integer
FIELD(11, 15, u32 vuimm); // Vector/SIMD: Immediate 5-bit unsigned integer
#undef FIELD
};
struct PPUThread {}; // register context but we do not need it here
// Opcode part
// auto-initialize by recursively assigning all handlers in an opcode array
template< template< typename > class Handler,
template< size_t > class Indexer,
size_t start_index,
size_t end_index >
struct OpcodeArrayRange
{
template< typename Owner >
static __forceinline void initialize(Owner & owner)
{
owner.array[start_index] = Handler< Indexer< start_index > >::handle;
OpcodeArrayRange< Handler, Indexer, start_index + 1, end_index >::initialize(owner);
}
};
// auto-initialize by assigning the last handler in an opcode array
template< template< typename > class Handler,
template< size_t > class Indexer,
size_t start_index >
struct OpcodeArrayRange < Handler, Indexer, start_index, start_index >
{
template< typename Owner >
static __forceinline void initialize(Owner & owner)
{
owner.array[start_index] = Handler< Indexer< start_index > >::handle;
}
};
template < size_t po >
struct OPCD // Primary opcode, used for Indexer in OpcodeArrayRange
{
};
#ifdef B_FORM
template < size_t po >
using B_Form = OPCD < po >;
template < size_t bo_bi >
struct B_Form_BOBI
{
};
#endif
#ifdef X_FORM
template < size_t po >
using X_Form = OPCD < po > ; // Primary opcode + Extended opcode + Record bit
template < size_t po, size_t xo, size_t rc >
struct X_Form_XO_Rc // Primary opcode + Extended opcode + Record bit
{
};
template < size_t po, size_t xo_rc >
struct X_Form_XORc // glue Extended opcode and Record bit into one field, used for Indexer in OpcodeArrayRange
{
};
template < size_t xo_rc >
using X_Form_31_XORc = X_Form_XORc < 31, xo_rc > ; // alias to X_Form_XORc with Primary opcode 31
#endif
// Interpreter part
template< typename T >
struct Interpreter
{
};
// generic interpreter array
template< template< size_t > class OpcodeFormat,
size_t start_index,
size_t end_index >
struct InterpretArray :
OpcodeArrayRange < Interpreter, OpcodeFormat, start_index, end_index >
{
InterpretArray()
{
initialize(*this);
}
__forceinline void operator()(size_t index,
Instruction code,
PPUThread& thread)
{
array[index](code, thread);
}
void(*array[1 + end_index - start_index])(Instruction, PPUThread&);
};
// implementation for interpreting opcodes
template< size_t po >
struct Interpreter < OPCD < po > >
{
static void handle(Instruction /*code*/, PPUThread& /*thread*/)
{
std::cout
<< "OPCD #"
<< po
<< std::endl;
}
};
#ifdef B_FORM
template < size_t bo_bi >
struct Interpreter < B_Form_BOBI < bo_bi > >
{
static void handle(Instruction code, PPUThread& thread)
{
std::cout
<< "OPCD #31, BO #"
<< (bo_bi >> 5)
<< ", BI #"
<< (bo_bi & 31)
<< ", AA #"
<< (code.aa)
<< ", LK #"
<< (code.lk)
<< std::endl;
}
};
static InterpretArray < B_Form_BOBI, 0, 0x3FF > interpret_B_Form_BOBI;
template< >
struct Interpreter < B_Form < 16 > >
{
static void handle(Instruction code, PPUThread& thread)
{
interpret_B_Form_BOBI((code.instruction >> 16) & 0x3FF, code, thread);
}
};
#endif
#ifdef X_FORM
template < size_t xo_rc >
struct Interpreter < X_Form_31_XORc < xo_rc > >
{
static void handle(Instruction /*code*/, PPUThread& /*thread*/)
{
std::cout
<< "OPCD #31, XO #"
<< (xo_rc >> 1)
<< ", Rc #"
<< (xo_rc & 1)
<< std::endl;
}
};
// specific interpreter array for instructions selected by their extended code
// and Record bit when primary opcode is 31
static InterpretArray < X_Form_31_XORc, 0, 0x7FF > interpret_X_Form_31;
template< >
struct Interpreter < X_Form < 31 > > // note that X_Form is an alias to OPCD
{
static void handle(Instruction code, PPUThread& thread)
{
interpret_X_Form_31((code.instruction & 0x7FF), code, thread);
}
};
#endif
// specific interpreter array for instructions
// selected by primary opcode
static InterpretArray < OPCD, 0, 0x3F > interpret;
int main()
{
Instruction insn;
PPUThread thread;
{
insn.opcode = 2;
interpret(insn.opcode, insn, thread);
}
#ifdef B_FORM
{
insn.opcode = 16;
insn.bo = 2;
insn.bi = 3;
insn.aa = 1;
insn.lk = 1;
interpret(insn.opcode, insn, thread);
}
#endif
#ifdef X_FORM
{
insn.opcode = 31;
insn.op31 = 2;
insn.rc = 0;
interpret(insn.opcode, insn, thread);
}
{
insn.opcode = 31;
insn.op31 = 2;
insn.rc = 1;
interpret(insn.opcode, insn, thread);
}
#endif
}
EDIT: I added B_Form which is similar to X_Form but not using alias and it works (but building is quite slow).
template< template< typename > class Handler,
template< size_t > class Indexer,
size_t start_index,
size_t end_index >
struct OpcodeArrayRange
{
template< typename Owner >
static __forceinline void initialize(Owner & owner)
{
owner.array[start_index] = Handler< Indexer< start_index > >::handle;
OpcodeArrayRange< Handler, Indexer, start_index + 1, end_index >::initialize(owner);
}
};
...
template< template< size_t > class OpcodeFormat,
size_t start_index,
size_t end_index >
struct InterpretArray :
OpcodeArrayRange < Interpreter, OpcodeFormat, start_index, end_index >
...
static InterpretArray < X_Form_31_XORc, 0, 0x7FF > interpret_X_Form_31;
The instantiation of InterpretArray < X_Form_31_XORc, 0, 0x7FF > triggers the instantiation of its base class OpcodeArrayRange < Interpreter, X_Form_31_XORc, 0, 0x7FF >.
The instantiation of OpcodeArrayRange < Interpreter, X_Form_31_XORc, 0, 0x7FF > triggers the instantiation of OpcodeArrayRange < Interpreter, X_Form_31_XORc, 1, 0x7FF >, because of how you defined your initialize method.
The instantiation of OpcodeArrayRange < Interpreter, X_Form_31_XORc, 1, 0x7FF > triggers the instantiation of OpcodeArrayRange < Interpreter, X_Form_31_XORc, 2, 0x7FF >, because of how you defined your initialize method.
The instantiation of OpcodeArrayRange < Interpreter, X_Form_31_XORc, 2, 0x7FF > triggers the instantiation of OpcodeArrayRange < Interpreter, X_Form_31_XORc, 3, 0x7FF >, because of how you defined your initialize method.
etc.
This recursive instantiation of templates is subject to limits. You've exceeded your compiler's limit (as well as GCC's and clang's). You probably simply shouldn't be referring to other template instantiations in your initialize method.
Consider following code
struct VersionData
{
VersionData();
VersionData(VersionData&& rhs);
int m_versionId;
int m_weight;
int m_pId;
bool m_hdi;
};
struct VersionId{};
typedef boost::multi_index_container<
VersionData,
bmi::indexed_by<
bmi::ordered_non_unique<
bmi::tag<VersionId>,
bmi::member<VersionData, int, &VersionData::m_versionId>
>
>
> VersionDataContainer;
struct VersionsData
{
VersionsData();
VersionsData(VersionsData&& rhs);
int m_sdGroupId;
int m_retId;
VersionDataContainer m_versionData;
};
struct mvKey{};
typedef boost::multi_index_container<
VersionsData,
bmi::indexed_by<
bmi::ordered_unique<
bmi::tag<mvKey>,
bmi::composite_key<
VersionsData,
bmi::member<VersionsData,int, &VersionsData::m_subdeliveryGroupId>,
bmi::member<VersionsData,int, &VersionsData::m_retargetingId>
>
>
>
> mvDataContainer;
mvDataContainer m_data;
The intention is to lookup using the composite key in mvDataContainer but in some cases I need to lookup in VersionData across all VersionsData. Something like m_data.get<mvKey>.equal_range(make_tuple(ignore, ignore, ignore)).get<VersionId>.equal_range(123456);
First question, is it achievable?
Second, is this the right approach to use nested multi_index_containers? any performance impacts/gains?
In addition to the other answer suggesting a single container for the whole table, here's the idea based on Boost Intrusive multiset
See it Live On Coliru
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/composite_key.hpp>
// for intrusive multiset
#include <boost/intrusive/set.hpp>
#include <boost/range/iterator_range.hpp>
#include <iostream>
namespace bmi = boost::multi_index;
namespace bi = boost::intrusive;
struct VersionData : bi::set_base_hook<bi::link_mode<bi::auto_unlink> > {
VersionData(int versionId, int weight=0, int pId=0, bool hdi=false) :
m_versionId(versionId), m_weight(weight), m_pId(pId), m_hdi(hdi) { }
int m_versionId;
int m_weight;
int m_pId;
bool m_hdi;
friend std::ostream& operator<<(std::ostream& os, VersionData const& vd) {
return os << "{ " << vd.m_versionId << " " << vd.m_weight << " " << vd.m_pId << " " << vd.m_hdi << " }";
}
struct ById {
bool operator()(VersionData const& a, VersionData const& b) const { return a.m_versionId < b.m_versionId; }
};
};
typedef bi::multiset<VersionData, bi::constant_time_size<false>, bi::compare<VersionData::ById> > VersionIndex;
typedef boost::multi_index_container<
VersionData,
bmi::indexed_by<
bmi::ordered_non_unique<
bmi::tag<struct VersionId>,
bmi::member<VersionData, int, &VersionData::m_versionId>
>
>
> VersionDataContainer;
struct VersionsData
{
int m_subdeliveryGroupId;
int m_retargetingId;
VersionDataContainer m_versionData;
};
typedef boost::multi_index_container<
VersionsData,
bmi::indexed_by<
bmi::ordered_unique<
bmi::tag<struct mvKey>,
bmi::composite_key<
VersionsData,
bmi::member<VersionsData,int, &VersionsData::m_subdeliveryGroupId>,
bmi::member<VersionsData,int, &VersionsData::m_retargetingId>
>
>
>
> mvDataContainer;
void insert(
mvDataContainer& into, VersionIndex& global_version_index,
int subdeliveryGroupId, int retargetingId, int
versionId, int weight, int pId, bool hdi)
{
auto& mainIdx = into.get<mvKey>();
auto insertion = mainIdx.insert(VersionsData { subdeliveryGroupId, retargetingId, VersionDataContainer {} });
mainIdx.modify(insertion.first, [&](VersionsData& record) {
auto insertion = record.m_versionData.insert(VersionData { versionId, weight, pId, hdi });
global_version_index.insert(const_cast<VersionData&>(*insertion.first));
});
}
int main() {
VersionIndex global_version_index;
mvDataContainer table;
insert(table, global_version_index, 21, 10, 1, 100, 123, false);
insert(table, global_version_index, 9, 27, 2, 90, 123, false);
insert(table, global_version_index, 12, 25, 3, 110, 123, true);
insert(table, global_version_index, 10, 33, /*version 8:*/ 8, 80, 123, false);
insert(table, global_version_index, 4, 38, 5, 101, 124, false);
insert(table, global_version_index, 33, 7, 6, 91, 124, false);
insert(table, global_version_index, 34, 27, 7, 111, 124, true);
insert(table, global_version_index, 9, 11, /*version 8:*/ 8, 81, 124, false);
insert(table, global_version_index, 0, 12, 9, 99, 125, false);
insert(table, global_version_index, 35, 39, /*version 8:*/ 8, 89, 125, false);
insert(table, global_version_index, 15, 15, 11, 109, 125, true);
insert(table, global_version_index, 25, 32, /*version 8:*/ 8, 79, 125, false);
// debug table output
assert(table.size()==12);
// so now you can do:
std::cout << "---\nQuerying for version id 8:\n";
for (auto& record : boost::make_iterator_range(global_version_index.equal_range(8)))
std::cout << record << "\n";
table.erase(table.find(boost::make_tuple(10,33))); // auto unlinks from global_version_index
std::cout << "---\nQuerying for version id 8:\n";
for (auto& record : boost::make_iterator_range(global_version_index.equal_range(8)))
std::cout << record << "\n";
}
Prints:
---
Querying for version id 8:
{ 8 80 123 0 }
{ 8 81 124 0 }
{ 8 89 125 0 }
{ 8 79 125 0 }
---
Querying for version id 8:
{ 8 81 124 0 }
{ 8 89 125 0 }
{ 8 79 125 0 }
So indeed, instead of using nested containers, like that (live on Coliru)
You could/should implement it as a single /table/ (after all, this is a table with several indices):
Live On Coliru
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/composite_key.hpp>
namespace bmi = boost::multi_index;
struct VersionRecord {
int m_subdeliveryGroupId;
int m_retargetingId;
int m_versionId;
int m_weight;
int m_pId;
bool m_hdi;
friend std::ostream& operator<<(std::ostream& os, VersionRecord const& record) {
return os << "{ " << record.m_subdeliveryGroupId << " " << record.m_retargetingId << " "
<< record.m_versionId << " " << record.m_weight << " " << record.m_pId << " " << record.m_hdi << " }";
}
};
typedef boost::multi_index_container<
VersionRecord,
bmi::indexed_by<
bmi::ordered_unique<
bmi::tag<struct mvKey>,
bmi::composite_key<
VersionRecord,
bmi::member<VersionRecord,int, &VersionRecord::m_subdeliveryGroupId>,
bmi::member<VersionRecord,int, &VersionRecord::m_retargetingId>
>
>,
bmi::ordered_non_unique<
bmi::tag<struct VersionId>,
bmi::member<VersionRecord, int, &VersionRecord::m_versionId>
>
>
> VersionTable;
#include <iostream>
#include <boost/range/iterator_range.hpp>
int main() {
auto table = VersionTable {
{ 21, 10, 1, 100, 123, false },
{ 9, 27, 2, 90, 123, false },
{ 12, 25, 3, 110, 123, true },
{ 10, 33, /*version 8:*/ 8, 80, 123, false },
{ 4, 38, 5, 101, 124, false },
{ 33, 7, 6, 91, 124, false },
{ 34, 27, 7, 111, 124, true },
{ 9, 11, /*version 8:*/ 8, 81, 124, false },
{ 0, 12, 9, 99, 125, false },
{ 35, 39, /*version 8:*/ 8, 89, 125, false },
{ 15, 15, 11, 109, 125, true },
{ 25, 32, /*version 8:*/ 8, 79, 125, false },
};
// debug table output
assert(table.size()==12);
for (auto& record : table) std::cout << record << "\n";
// so now you can do:
auto& idx = table.get<VersionId>();
std::cout << "---\nQuerying for version id 8:\n";
for (auto& record : boost::make_iterator_range(idx.equal_range(8)))
std::cout << record << "\n";
}
Which prints:
{ 0 12 9 99 125 0 }
{ 4 38 5 101 124 0 }
{ 9 11 8 81 124 0 }
{ 9 27 2 90 123 0 }
{ 10 33 8 80 123 0 }
{ 12 25 3 110 123 1 }
{ 15 15 11 109 125 1 }
{ 21 10 1 100 123 0 }
{ 25 32 8 79 125 0 }
{ 33 7 6 91 124 0 }
{ 34 27 7 111 124 1 }
{ 35 39 8 89 125 0 }
---
Querying for version id 8:
{ 25 32 8 79 125 0 }
{ 35 39 8 89 125 0 }
{ 10 33 8 80 123 0 }
{ 9 11 8 81 124 0 }
Alternatively, you can bolt an intrusive container on top of the VersionsData records. This however, complicates the design (you either have to use auto_unlink node hooks (sacrificing thread safety control) or you have to make sure the containers are in synch all the time.
It is not the exact answer which I originally asked but since the performance issue was mentioned and in light of discussion with #sehe this is what I found.
1) use flat structure, you can save wasting memory on the same keys using boost::flyweight
2) use MIC instead of tailored containers, MIC might be slightly slower (depends on test scenario) when searching on simple indexes, but once you use composite keys (and implement similar behavior for your tailored datastructure) it is from slightly to significantly faster than tailored DS
My previous statement that tailored one is faster is wrong, since I was using MIC from boost 1.52 and looks like there was a bug when using composite keys with strings (5 orders of magnitude slower than composite without string). When switched to 1.57 everything started to work as expected.
Tests on Coliru
Have a nice indexing, guys! :)