The problem I am trying to solve is that, for readability of my code, I would like to use string literals instead of numerals.
These should be converted to numerals at compile time (without additional preprocessing of the code).
In principle this should not be a problem nowadays, and actually the following seems to work:
constexpr unsigned long bogus_hash(char const *input) {
return input[0]+input[89];
}
constexpr unsigned long compute_hash(const char* a) {
return bogus_hash(a);
}
class HashedString {
public:
constexpr HashedString(const char* a): my_hash(compute_hash(a)) {};
constexpr HashedString(unsigned long a): my_hash(a) {};
constexpr unsigned long const& get() const {return my_hash;}
constexpr bool operator ==(const char* b) {
return my_hash == compute_hash(b);
};
protected:
unsigned long my_hash;
};
Almost: Of course, the hash function is use is not a good hash function. (The 89 in there is due to my test code:
int fun_new(HashedString a) {
return (a == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab");
}
int fun_old(std::string a) {
return (a == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab");
}
//#define CHOOSE fun_old
#define CHOOSE fun_new
int main() {
long res = 0;
for (long i = 0; i < 1000*1000*100; ++i) {
res += CHOOSE("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac"); // will add 1
res += CHOOSE("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"); // will add 0
}
std::cout << "res = " << res << std::endl; // should return number of iterations in loop
return 0;
}
(Note that the internal use of hashes instead of the string literal is transparent to the caller of the function.)
My questions are:
Does the code actually do what I want it to? Timing the code shows that it does run much faster when using HashedString (fun_new) instead of std::string (fun_old). (I am using g++ 4.8.2, with options -std=c++0x -O0. Using -O1 seems to get rid of the loop completely?)
Is there a compile-time hash function I can use for this purpose? All other (constexpr) hash functions I tried made the code run slower than before (likely because the constexpr is not evaluated at compile but at run-time.) I tried to use these: 1, 2, 3
Related
In the following example I would like to be told at compile time that the conversion from long to int changes the value just like I do if I don't use the user defined literal.
#include <cassert>
constexpr int operator "" _asInt(unsigned long long i) {
// How do I ensure that i fits in an int here?
// assert(i < std::numeric_limits<int>::max()); // i is not constexpr
return static_cast<int>(i);
}
int main() {
int a = 1_asInt;
int b = 99999999999999999_asInt; // I'd like a warning or error here
int c = 99999999999999999; // The compiler will warn me here that this isn't safe
}
I can work out a few ways of getting a runtime error but I'm hoping there is some way to make it a compile time error since as far as I can see all of the elements can be known at compile time.
Make it consteval:
consteval int operator "" _asInt(unsigned long long i) {
if (i > (unsigned long long) std::numeric_limits<int>::max()) {
throw "nnn_asInt: too large";
}
return i;
}
int main() {
int a = 1_asInt;
// int b = 99999999999999999_asInt; // Doesn't compile
int c = 99999999999999999; // Warning
}
In C++17, you can use a literal operator template, but it's a bit more involved:
template<char... C>
inline constexpr char str[sizeof...(C)] = { C... };
// You need to implement this
constexpr unsigned long long parse_ull(const char* s);
template<char... S>
constexpr int operator "" _asInt() {
constexpr unsigned long long i = parse_ull(str<S..., 0>);
static_assert(i <= std::numeric_limits<int>::max(), "nnn_asInt: too large");
return int{i};
}
I understand that code will be slower, but why so much? How do I code to avoid this slowdown?
std::unordered_map uses other containers internally and those containers use iterators. When built debug, _ITERATOR_DEBUG_LEVEL=2 by default. This turns on iterator debugging. Sometimes my code is not affected much, and sometimes it runs extremely slowly.
I can speed my example up by setting _ITERATOR_DEBUG_LEVEL=0 in my project properties >> C++ >> Preprocessor >> Preprocessor definitions. But as this link suggests, I cannot do so in my real project. In my case, I get conflicts with MSVCMRTD.lib, which contains std::basic_string built with _ITERATOR_DEBUG_LEVEL=2. I understand I can work around the problem by statically linking to the CRT. But I would prefer not to if I can fix the code so the problem does not arise.
I can make changes that improve the situation. But I am just trying things out without understanding why they work. For example, as is, the first 1000 inserts work at full speed. But if I change O_BYTE_SIZE to 1, the first inserts are as slow as everything else. This looks like a small change (not necessarily a good change.)
This, this, and this also shed some light, but don't answer my question.
I am using Visual Studio 2010 (This is legacy code.) I created a Win32 console app and added this code.
Main.cpp
#include "stdafx.h"
#include "OString.h"
#include "OTHashMap.h"
#include <cstdio>
#include <ctime>
#include <iostream>
// Hash and equal operators for map
class CRhashKey {
public:
inline unsigned long operator() (const OString* a) const { return a->hash(); }
};
class CReqKey {
public:
inline bool operator() (const OString& x, const OString& y) const { return strcmp(x.data(),y.data()) != 0; }
inline bool operator() (const OString* x, const OString& y) const { return operator()(*x,y); }
inline bool operator() (const OString& x, const OString* y) const { return operator()(x,*y); }
inline bool operator() (const OString* x, const OString* y) const { return operator()(*x,*y); }
};
int _tmain(int argc, _TCHAR* argv[])
{
const int CR_SIZE = 1020007;
CRhashKey h;
OTPtrHashMap2<OString, int, CRhashKey, CReqKey> *code_map =
new OTPtrHashMap2 <OString, int, CRhashKey, CReqKey>(h, CR_SIZE);
const clock_t begin_time = clock();
for (int i=1; i<=1000000; ++i)
{
char key[10];
sprintf(key, "%d", i);
code_map->insert(new OString(key), new int(i));
//// Check hash values
//OString key2(key);
//std::cout << i << "\t" << key2.hash() << std::endl;
// Check timing
if ((i % 100) == 0)
{
std::cout << i << "\t" << float(clock() - begin_time) / CLOCKS_PER_SEC << std::endl;
}
}
std::cout << "Press enter to exit" << std::endl;
char buf[256];
std::cin.getline(buf, 256);
return 0;
}
OTHashMap.h
#pragma once
#include <fstream>
#include <unordered_map>
template <class K, class T, class H, class EQ>
class OTPtrHashMap2
{
typedef typename std::unordered_map<K*,T*,H,EQ> OTPTRHASHMAP_INTERNAL_CONTAINER;
typedef typename OTPTRHASHMAP_INTERNAL_CONTAINER::iterator OTPTRHASHMAP_INTERNAL_ITERATOR;
public:
OTPtrHashMap2(const H& h, size_t defaultCapacity) : _hashMap(defaultCapacity, h) {}
bool insert(K* key, T* val)
{
std::pair<OTPTRHASHMAP_INTERNAL_ITERATOR,T> retVal = _hashMap.insert(std::make_pair<K*,T*>(key, val));
return retVal.second != NULL;
}
OTPTRHASHMAP_INTERNAL_CONTAINER _hashMap;
private:
};
OString.h
#pragma once
#include <string>
class OString
{
public:
OString(const std::string& s) : _string (s) { }
~OString(void) {}
static unsigned hash(const OString& s) { return unsigned (s.hash()); }
unsigned long hash() const
{
unsigned hv = static_cast<unsigned>(length());
size_t i = length() * sizeof(char) / sizeof(unsigned);
const char * p = data();
while (i--) {
unsigned tmp;
memcpy(&tmp, p, sizeof(unsigned));
hashmash(hv, tmp);
p = p + sizeof(unsigned);
}
if ((i = length() * sizeof(char) % sizeof(unsigned)) != 0) {
unsigned h = 0;
const char* c = reinterpret_cast<const char*>(p);
while (i--)
{
h = ((h << O_BYTE_SIZE*sizeof(char)) | *c++);
}
hashmash(hv, h);
}
return hv;
}
const char* data() const { return _string.c_str(); }
size_t length() const { return _string.length(); }
private:
std::string _string;
//static const unsigned O_BYTE_SIZE = 1;
static const unsigned O_BYTE_SIZE = 8;
static const unsigned O_CHASH_SHIFT = 5;
inline void hashmash(unsigned& hash, unsigned chars) const
{
hash = (chars ^
((hash << O_CHASH_SHIFT) |
(hash >> (O_BYTE_SIZE*sizeof(unsigned) - O_CHASH_SHIFT))));
}
};
I found enough of an answer. Collisions are the source of slowing.
Edit 2: -- Another fix is to add this around the #include in main.cpp --
// Iterator debug checking makes the Microsoft implementation of std containers
// *very* slow in debug builds for large containers. It must only be undefed around
// STL includes. Otherwise we get linker errors from the debug C runtime library,
// which was built with _ITERATOR_DEBUG_LEVEL set to 2.
#ifdef _DEBUG
#undef _ITERATOR_DEBUG_LEVEL
#endif
#include <unordered_map>
#ifdef _DEBUG
#define _ITERATOR_DEBUG_LEVEL 2
#endif
Edit: -- The fix is switch to boost::unordered_map. --
std::unordered_map is defined in < unordered_map >. It inherits from _Hash, defined in < xhash >.
_Hash contains this (highly abbreviated)
template<...>
class _Hash
{
typedef list<typename _Traits::value_type, ...> _Mylist;
typedef vector<iterator, ... > _Myvec;
_Mylist _List; // list of elements, must initialize before _Vec
_Myvec _Vec; // vector of list iterators, begin() then end()-1
};
All values are stored in _List.
_Vec is a vector of iterators into _List. It divides _List into buckets. _Vec has an iterator to the beginning and end of each bucket. Thus, if the map has 1M buckets (distinct key hashes), _Vec has 2M iterators.
When a key/value pair is inserted into the map, usually a new bucket is created. The value is pushed onto the beginning of the list. The hash of the key is the location in _Vec where two new iterators are put. This is quick because they point to the beginning of the list.
If a bucket already exists, the new value must be inserted next to the existing value in _List. This requires inserting an item in the middle of the list. Existing iterators must be updated. Apparently this requires a lot of work when iterator debugging is enabled. The code is in < list >, but I did not step through it.
To get an idea of how much work, I used some nonsense hash functions that would be terrible to use, but give lots of collisions or few collisions when inserting.
Added to OString.h
static unsigned hv2;
// Never collides. Always uses the next int as the hash
unsigned long hash2() const
{
return ++hv2;
}
// Almost never collides. Almost always gets the next int.
// Gets the same int 1 in 200 times.
unsigned long hash3() const
{
++hv2;
unsigned long lv = (hv2*200UL)/201UL;
return (unsigned)lv;
}
// A best practice hash
unsigned long hash4() const
{
std::hash<std::string> hasher;
return hasher(_string);
}
// Always collides. Everything into bucket 0.
unsigned long hash5() const
{
return 0;
}
Added to main.cpp
// Hash and equal operators for map
class CRhashKey {
public:
//inline unsigned long operator() (const OString* a) const { return a->hash(); }
//inline unsigned long operator() (const OString* a) const { return a->hash2(); }
//inline unsigned long operator() (const OString* a) const { return a->hash3(); }
//inline unsigned long operator() (const OString* a) const { return a->hash4(); }
inline unsigned long operator() (const OString* a) const { return a->hash5(); }
};
unsigned OString::hv2 = 0;
The results were dramatic. No realistic hash is going to work.
hash2 - Never collide - 1M inserts in 15.3 sec
hash3 - Almost never - 1M inserts in 206 sec
hash4 - Best practice - 100k inserts in 132 sec, and getting slower as collisions became more frequent. 1M inserts would take > 1 hour
hash5 - Always collide - 1k inserts in 48 sec, or 1M inserts in ~13 hours
My choices are
Release build, debug symbols, optimization off as Retired Ninja suggests
Statically link to MSVCMRTD so I can turn off _ITERATOR_DEBUG_LEVEL. Also solve some other similar issues.
Change from unordered_map to a sorted vector.
Something else. Suggestions welcome.
The situation: occasionally I write a function that can take a number of boolean parameters, and instead of writing something like this:
void MyFunc(bool useFoo, bool useBar, bool useBaz, bool useBlah);
[...]
// hard to tell what this means (requires looking at the .h file)
// not obvious if/when I got the argument-ordering wrong!
MyFunc(true, true, false, true);
I like to be able to specify them using a bit-chord of defined bits-indices, like this:
enum {
MYARG_USE_FOO = 0,
MYARG_USE_BAR,
MYARG_USE_BAZ,
MYARG_USE_BLAH,
NUM_MYARGS
};
void MyFunc(unsigned int myArgsBitChord);
[...]
// easy to see what this means
// "argument" ordering doesn't matter
MyFunc((1<<MYARG_USE_FOO)|(1<<MYARG_USE_BAR)|(1<<MYARG_USE_BLAH));
That works fine, in that it allows me to pass around a lot of boolean arguments easily (as a single unsigned long, rather than a long list of separate bools), and I can easy see what my call to MyFunc() is specifying (without having to refer back to a separate header file).
It also allows me to iterate over the defined bits if I want to, which is sometimes useful:
unsigned int allDefinedBits = 0;
for (int i=0; i<NUM_MYARGS; i++) allDefinedBits |= (1<<i);
The main downsides are that it can be a bit error-prone. In particular, it's easy to mess up and do this by mistake:
// This will compile but do the wrong thing at run-time!
void MyFunc(MYARG_USE_FOO | MYARG_USE_BAR | MYARG_USE_BLAH);
... or even to make this classic forehead-slapping mistake:
// This will compile but do the wrong thing at run-time!
void MyFunc((1<<MYARG_USE_FOO) | (1<<MYARG_USE_BAR) || (1<<MYARG_USE_BLAH));
My question is, is there a recommended "safer" way to do this? i.e. one where I can still easily pass multiple defined booleans as a bit-chord in a single argument, and can iterate over the defined bit-values, but where "dumb mistakes" like the ones shown above will be caught by the compiler rather than causing unexpected behavior at runtime?
#include <iostream>
#include <type_traits>
#include <cstdint>
enum class my_options_t : std::uint32_t {
foo,
bar,
baz,
end
};
using my_options_value_t = std::underlying_type<my_options_t>::type;
inline constexpr auto operator|(my_options_t const & lhs, my_options_t const & rhs)
{
return (1 << static_cast<my_options_value_t>(lhs)) | (1 << static_cast<my_options_value_t>(rhs));
}
inline constexpr auto operator|(my_options_value_t const & lhs, my_options_t const & rhs)
{
return lhs | (1 << static_cast<my_options_value_t>(rhs));
}
inline constexpr auto operator&(my_options_value_t const & lhs, my_options_t const & rhs)
{
return lhs & (1 << static_cast<my_options_value_t>(rhs));
}
void MyFunc(my_options_value_t options)
{
if (options & my_options_t::bar)
std::cout << "yay!\n\n";
}
int main()
{
MyFunc(my_options_t::foo | my_options_t::bar | my_options_t::baz);
}
How about a template...
#include <iostream>
template <typename T>
constexpr unsigned int chordify(const T& v) {
return (1 << v);
}
template <typename T1, typename... Ts>
constexpr unsigned int chordify(const T1& v1, const Ts&... rest) {
return (1 << v1) | chordify(rest... );
}
enum {
MYARG_USE_FOO = 0,
MYARG_USE_BAR,
MYARG_USE_BAZ,
MYARG_USE_BLAH,
NUM_MYARGS
};
int main() {
static_assert(__builtin_constant_p(
chordify(MYARG_USE_FOO, MYARG_USE_BAZ, MYARG_USE_BLAH)
));
std::cout << chordify(MYARG_USE_FOO, MYARG_USE_BAZ, MYARG_USE_BLAH);
}
That outputs 13, and it's a compile-time constant.
You can use a bit field, which allows you to efficiently construct a structure with individually-named bit flags.
for example, you could pass a struct myOptions to the function, where it is defined as:
struct myOptions {
unsigned char foo:1;
unsigned char bar:1;
unsigned char baz:1;
};
Then, when you have to construct the values to send to the function, you'd do something like this:
myOptions opt;
opt.foo = 1;
opt.bar = 0;
opt.baz = 1;
MyFunct(opt);
Bit fields are compact and efficient, yet allow you to name the bits (or groups of bits) as if they were independent variables.
By the way, given the verbosity of the declaration, this is one place where I might break the common style of only declaring one variable per statement, and declare the struct as follows:
struct myOptions {
unsigned char foo:1, bar:1, baz:1;
};
And, in C++20 you can add initializers:
struct myOptions {
unsigned char foo:1{0}, bar:1{0}, baz:1{0};
}
First, I am new to googletest framework so be kind.
I have a function
void setID(const int id)
{
ID = id;
}
where ID is a global unsigned int. (Yes, globals are bad, I am just trying to figure what I am doing.)
My unit test looks like this
TEST_F(TempTests, SetId)
{
// Arrange
int id = -99;
// Act
setId(id);
// Assert
EXPECT_EQ(id, ID);
}
The problem is my unit test always passes and I need it to fail as ID should have been a signed int not an unsigned int. If I hadn't visually caught the error the unit test would have passed and it could have caused errors later on.
To make sure that this doesn't happen in the future it would be best if the unit test comparison failed in this case.
I have tried static casting id and ID to signed and unsigned ints in various ways.
I have tried doing EXPECT_TRUE(id == ID) with static casting the variables to signed and unsigned ints in various ways.
But in all of these cases the result is a passing test.
So how can I get gtest to compare the signed value of id and unsigned value of ID so that the test will fail because id will be -99 and ID will be 4294967197?
The compiler will need to convert the types to be equal. I recommend reading this related answer.
You might be able to create a custom googletest comparator. Even if not, you can at the very least use something similar like such:
#include <iostream>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <typeinfo>
template <class T>
class SignedUnsignedIntCompare final /* final for non-virtual dtor, remember to make dtor virtual if you need to inherit */ {
public:
const T & v;
SignedUnsignedIntCompare(const T & v) : v(v) {}
SignedUnsignedIntCompare(SignedUnsignedIntCompare && move_ctor) = default;
SignedUnsignedIntCompare(const SignedUnsignedIntCompare & copy_ctor) = default;
SignedUnsignedIntCompare & operator=(SignedUnsignedIntCompare && move_assign) = default;
SignedUnsignedIntCompare & operator=(const SignedUnsignedIntCompare & copy_assign) = default;
~SignedUnsignedIntCompare() = default;
template <class TT>
bool operator==(const TT & i) const {
if ( std::numeric_limits<T>::is_signed != std::numeric_limits<TT>::is_signed ) {
return ((v == i) && (T(v) <= std::numeric_limits<TT>::max()) && (TT(i) <= std::numeric_limits<T>::max()));
}
return (v == i);
}
};
typedef SignedUnsignedIntCompare<int> SignedIntCompare;
typedef SignedUnsignedIntCompare<unsigned> UnsignedIntCompare;
int main() {
int i = -99;
unsigned int u = i;
std::cout << (i == u) << " vs " << (SignedIntCompare(i) == u) << std::endl;
return 0;
}
At that point, you can then use EXPECT_TRUE or similar boolean checks, like such:
TEST(foo, bar) {
int id = -99;
setId(id);
EXPECT_TRUE(SignedUnsignedIntCompare<decltype(ID)>(ID) == id);
}
So I'm not sure how to give credit, but I ended up using a combination of inetknght's and mkk's suggestions.
TEST_F(TempTests, SetId)
{
// Arrange
int id = -99;
// Act
setId(id);
// Assert
EXPECT_TRUE(std::numeric_limits<decltype(id)>::is_signed == std::numeric_limits<decltype(ID)>::is_signed);
EXPECT_EQ(id, ID);
}
Per inetknght's suggestion, by checking the signed types I am able to fail the test as the types are not both signed. And per mkk's suggestion by using decltype I can get the declared types of the variables without modifying the unit test in the future when the type of ID is corrected. When the type of ID is corrected then the test passes.
Edit
Per Adrian McCarthy's suggestion I have also added -Werror=conversion to my compiler flags.
Is it possible to perform a unique string to int mapping at compile time?
Let's say I have a template like this for profiling:
template <int profilingID>
class Profile{
public:
Profile(){ /* start timer */ }
~Profile(){ /* stop timer */ }
};
which I place at the beginning of function calls like this:
void myFunction(){
Profile<0> profile_me;
/* some computations here */
}
Now I'm trying to do something like the following, which is not possible since string literals cannot be used as a template argument:
void myFunction(){
Profile<"myFunction"> profile_me; // or PROFILE("myFunction")
/* some computations here */
}
I could declare global variables to overcome this issue, but I think it would be more elegant to avoid previous declarations. A simple mapping of the form
”myFunction” → 0
”myFunction1” → 1
…
”myFunctionN” → N
would be sufficient. But to this point neither using constexpr, template meta-programming nor macros I could find a way to accomplish such a mapping. Any ideas?
As #harmic has already mentioned in the comments, you should probably just pass the name to the constructor. This might also help reduce code bloat because you don't generate a new type for each function.
However, I don't want to miss the opportunity to show a dirty hack that might be useful in situations where the string cannot be passed to the constructor. If your strings have a maximum length that is known at compile-time, you can encode them into integers. In the following example, I'm only using a single integer which limits the maximum string length to 8 characters on my system. Extending the approach to multiple integers (with the splitting logic conveniently hidden by a small macro) is left as an exercise to the reader.
The code makes use of the C++14 feature to use arbitrary control structures in constexpr functions. In C++11, you'd have to write wrap as a slightly less straight-forward recursive function.
#include <climits>
#include <cstdint>
#include <cstdio>
#include <type_traits>
template <typename T = std::uintmax_t>
constexpr std::enable_if_t<std::is_integral<T>::value, T>
wrap(const char *const string) noexcept
{
constexpr auto N = sizeof(T);
T n {};
std::size_t i {};
while (string[i] && i < N)
n = (n << CHAR_BIT) | string[i++];
return (n << (N - i) * CHAR_BIT);
}
template <typename T>
std::enable_if_t<std::is_integral<T>::value>
unwrap(const T n, char *const buffer) noexcept
{
constexpr auto N = sizeof(T);
constexpr auto lastbyte = static_cast<char>(~0);
for (std::size_t i = 0UL; i < N; ++i)
buffer[i] = ((n >> (N - i - 1) * CHAR_BIT) & lastbyte);
buffer[N] = '\0';
}
template <std::uintmax_t Id>
struct Profile
{
char name[sizeof(std::uintmax_t) + 1];
Profile()
{
unwrap(Id, name);
std::printf("%-8s %s\n", "ENTER", name);
}
~Profile()
{
std::printf("%-8s %s\n", "EXIT", name);
}
};
It can be used like this:
void
function()
{
const Profile<wrap("function")> profiler {};
}
int
main()
{
const Profile<wrap("main")> profiler {};
function();
}
Output:
ENTER main
ENTER function
EXIT function
EXIT main
In principle you can. However, I doubt any option is practical.
You can set your key type to be a constexpr value type (this excludes std::string), initializing the value type you implement is not a problem either, just throw in there a constexpr constructor from an array of chars. However, you also need to implement a constexpr map, or hash table, and a constexpr hashing function. Implementing a constexpr map is the hard part. Still doable.
You could create a table:
struct Int_String_Entry
{
unsigned int id;
char * text;
};
static const Int_String_Entry my_table[] =
{
{0, "My_Function"},
{1, "My_Function1"},
//...
};
const unsigned int my_table_size =
sizeof(my_table) / sizeof(my_table[0]);
Maybe what you want is a lookup table with function pointers.
typedef void (*Function_Pointer)(void);
struct Int_vs_FP_Entry
{
unsigned int func_id;
Function_Point p_func;
};
static const Int_vs_FP_Entry func_table[] =
{
{ 0, My_Function},
{ 1, My_Function1},
//...
};
For more completion, you can combine all three attributes into another structure and create another table.
Note: Since the tables are declared as "static const", they are assembled during compilation time.
Why not just use an Enum like:
enum ProfileID{myFunction = 0,myFunction1 = 1, myFunction2 = 2 };
?
Your strings will not be loaded in runtime, so I don't understand the reason for using strings here.
It is an interesting question.
It is possible to statically-initialize a std::map as follows:
static const std::map<int, int> my_map {{1, 2}, {3, 4}, {5, 6}};
but I get that such initialization is not what you are looking for, so I took another approach after looking at your example.
A global registry holds a mapping between function name (an std::string) and run time (an std::size_t representing the number of milliseconds).
An AutoProfiler is constructed providing the name of the function, and it will record the current time. Upon destruction (which will happen as we exit the function) it will calculate the elapsed time and record it in the global registry.
When the program ends we print the contents of the map (to do so we utilize the std::atexit function).
The code looks as follows:
#include <cstdlib>
#include <iostream>
#include <map>
#include <chrono>
#include <cmath>
using ProfileMapping = std::map<std::string, std::size_t>;
ProfileMapping& Map() {
static ProfileMapping map;
return map;
}
void show_profiles() {
for(const auto & pair : Map()) {
std::cout << pair.first << " : " << pair.second << std::endl;
}
}
class AutoProfiler {
public:
AutoProfiler(std::string name)
: m_name(std::move(name)),
m_beg(std::chrono::high_resolution_clock::now()) { }
~AutoProfiler() {
auto end = std::chrono::high_resolution_clock::now();
auto dur = std::chrono::duration_cast<std::chrono::milliseconds>(end - m_beg);
Map().emplace(m_name, dur.count());
}
private:
std::string m_name;
std::chrono::time_point<std::chrono::high_resolution_clock> m_beg;
};
void foo() {
AutoProfiler ap("foo");
long double x {1};
for(std::size_t k = 0; k < 1000000; ++k) {
x += std::sqrt(k);
}
}
void bar() {
AutoProfiler ap("bar");
long double x {1};
for(std::size_t k = 0; k < 10000; ++k) {
x += std::sqrt(k);
}
}
void baz() {
AutoProfiler ap("baz");
long double x {1};
for(std::size_t k = 0; k < 100000000; ++k) {
x += std::sqrt(k);
}
}
int main() {
std::atexit(show_profiles);
foo();
bar();
baz();
}
I compiled it as:
$ g++ AutoProfile.cpp -std=c++14 -Wall -Wextra
and obtained:
$ ./a.out
bar : 0
baz : 738
foo : 7
You do not need -std=c++14, but you will need at least -std=c++11.
I realize this is not what you are looking for, but I liked your question and decided to pitch in my $0.02.
And notice that if you use the following definition:
using ProfileMapping = std::multi_map<std::string, std::size_t>;
you can record every access to each function (instead of ditching the new results once the first entry has been written, or overwriting the old results).
You could do something similar to the following. It's a bit awkward, but may do what you want a little more directly than mapping to an integer:
#include <iostream>
template <const char *name>
class Profile{
public:
Profile() {
std::cout << "start: " << name << std::endl;
}
~Profile() {
std::cout << "stop: " << name << std::endl;
}
};
constexpr const char myFunction1Name[] = "myFunction1";
void myFunction1(){
Profile<myFunction1Name> profile_me;
/* some computations here */
}
int main()
{
myFunction1();
}