std::string not working with std::set - c++

I'm doing a programming question from C++ Primer Plus which asks me to make a template
function that returns the number of unique elements in an array. I don't understand why
line 13 causes an error while compiling as to my knowledge, a std::string behaves like an array.
This is my code:
#include <iostream>
#include <set>
template <typename T>
int reduce(T ar[], int n);
int main()
{
long test[] = {1, 2, 1, 3, 3, 4, 1};
std::string testStr = "testing";
std::cout << reduce(test, 6) << std::endl;
std::cout << reduce(testStr, 7) << std::endl;
std::cin.get();
return 0;
}
template <typename T>
int reduce(T ar[], int n)
{
std::set<T> test;
for(int i = 0; i < n; i++)
{
test.insert(ar[i]);
}
return test.size();
}

Following up my immediate response that std::string is not an array, this is the way a C++ person might accomplish the task you're looking for.
#include <iterator>
#include <iostream>
#include <set>
// instead of taking an array and length, just take where you want to start and where
// you want to stop.
template <typename TForwardIterator>
int reduce(TForwardIterator iter, TForwardIterator end)
{
// This is hideous syntax to get the type of object the iterator is describing.
// For std::string, it is char...for T*, it is T.
// I apologize for C++, I'm not sure there is a better way to do this.
typedef typename std::iterator_traits<TForwardIterator>::value_type value_type;
std::set<value_type> set;
// instead of forcing the objects to be an array type, use iterators!
for (; iter != end; ++iter)
set.insert(*iter);
return set.size();
}
int main()
{
long test[] = {1, 2, 1, 3, 3, 4, 1};
std::string testStr = "testing";
// begin() and end() are iterators you'll find on all the container types
std::cout << reduce(testStr.begin(), testStr.end()) << std::endl;
// pointers are iterators, too!
std::cout << reduce(test, test + 7) << std::endl;
return 0;
}

The answer is quite simple: std::string is not an array.
It behaves like an array so far as you can access the elements using the [] operator, but it is simply not the same data type as char[]. As a matter of fact the standard doesn't even guarantee that it's stored like an array (meaning continously). T[] will only match to array of, not objects which can be used arraylike.
In order to solve this you have several options
you can call reduce(teststr.c_str(), 7), since c_str() will return an chararray with the contents of the string.
You could rewrite reduce as template <typename T, typename U> int reduce(U ar, int n) and call it as reduce<long>(test, 6) and reduce<char>(testStr, 7). The second template parameter is necessary, since there is no unified way to get from the container to the element (except in c++0x/using compiler extensions).
If you are using c++0x you can use decltype to get from a container to the contained element: template <typename T>int reduce(T ar, int n) and std::set<decltype(ar[0])> test; (rest of the code remains unchanged, and somehow I seem to have trouble with code block sections so just these two lines here.
Of course in c++ one would typically write such a function in terms of iterators (see Travis Gockels answer), since that's simply a more flexible and better supported way.

You may be confusing std::strings with built-in character arrays. std::strings are not arrays, though they behave similarly to arrays (the class has an overloaded [] operator) and contain arrays (which you can access through c_str()).
If you replace line 10 with
char testStr[] = "testing";
Your program will compile and run.
Or, you could try something like:
#include <iostream>
#include <set>
template <typename T>
int reduce(const T* ar, int n);
int main()
{
long test[] = {1, 2, 1, 3, 3, 4, 1};
std::string testStr = "testing";
std::cout << reduce(test, 7) << std::endl;
std::cout << reduce(testStr.c_str(), testStr.size()) << std::endl;
std::cin.get();
return 0;
}
template <typename T>
int reduce (const T* ar, int n)
{
std::set<T> test;
for(int i = 0; i < n; i++)
{
test.insert(ar[i]);
}
return test.size();
}

Related

use of algorithms in function cause ERROR: no instance of overloaded function

I'm trying to use an algorithm in a function.
Should be very simple.
However, regardless of which algorithm I attempt to use, all of them cause the same error when used in a function.
E0304 no instance of overloaded function "std::begin" matches the argument list
E0304 no instance of overloaded function "std::end" matches the argument list
I am guessing there is some small change that needs to be made.
#include <iostream>
#include <algorithm>
#include "bool_element_option_03.h"
#include "storage.h"
int main()
{
int arr_value[ELEMENTS]{ 1, 2, 9, 4, 5, 6, 7, 8 };
int arr_copy_value[ELEMENTS];
// array population
for (int var_create_array_a = 0; var_create_array_a < ELEMENTS; var_create_array_a++)
{
arr_copy_value[var_create_array_a] = 0;
}
//std::copy(std::begin(arr_value), std::end(arr_value), std::begin(arr_copy_value));
//std::sort(std::rbegin(arr_copy_value), std::rend(arr_copy_value));
for (int output = 0; output < ELEMENTS; output++)
{
std::cout << "copied decimals: " << arr_copy_value[output] << std::endl;
}
bool_element_option_03(arr_value, arr_copy_value);
return 0;
}
#ifndef _STORAGE_H
#define _STORAGE_H
#define WIN32_LEAN_AND_MEAN
// -----------------------------------------------------------------------------------------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------
const int ELEMENTS = 8;
//-----------------------------------------------
#endif
#include <iostream>
#include <algorithm>
#include "storage.h"
void bool_element_option_03(int arr_value[], int* arr_copy_value)
{
std::copy(std::begin(arr_value + ELEMENTS), std::end(arr_value + ELEMENTS), std::begin(arr_copy_value + ELEMENTS));
std::sort(std::rbegin(arr_copy_value + ELEMENTS), std::rend(arr_copy_value + ELEMENTS));
for (int output = 0; output < ELEMENTS; output++)
{
std::cout << "copied decimals: " << arr_copy_value[output] << std::endl;
}
}
If I take these algorithms out of the function and put them in main(), they work as they should.
Should I intentionally overload this function (so I can use algorithms in it)?
Overloading this function is not my intention.
I'm not calling it multiple times with different arguments.
This function is only being called once.
The error text is explicit:
E0304 no instance of overloaded function "std::begin" matches the argument list
So, what are those instances? See e.g. https://en.cppreference.com/w/cpp/iterator/begin
template< class C >
constexpr auto begin( C& c ) -> decltype(c.begin());
template< class C >
constexpr auto begin( const C& c ) -> decltype(c.begin());
Returns exactly c.begin(), which is typically an iterator to the beginning of the sequence represented by c. If C is a standard Container, this returns C::iterator when c is not const-qualified, and C::const_iterator otherwise.
template< class T, std::size_t N >
constexpr T* begin( T (&array)[N] ) noexcept;
Returns a pointer to the beginning of the array.
The last one seems promising, its argument is a reference to an array (note how it's declared) and that is the overload used here:
int main()
{
int arr_value[ELEMENTS]{ 1, 2, 9, 4, 5, 6, 7, 8 };
int arr_copy_value[ELEMENTS];
// ...
std::copy(std::begin(arr_value), // <---
std::end(arr_value),
std::begin(arr_copy_value)); // <---
// ...
}
That's because both arr_value and arr_copy_value are arrays, there.
When the compiler reads the refactored code, on the other end, it can't find a suitable overload:
void bool_element_option_03(int arr_value[], int* arr_copy_value)
{ // ^^ That's kind of misleading
std::copy(std::begin(arr_value + ELEMENTS),
std::end(arr_value + ELEMENTS),
std::begin(arr_copy_value + ELEMENTS));
// ...
}
Here, both arr_value and arr_copy_values are pointers, not arrays and there's no overload of std::begin() (and the likes) accepting a pointer in the Standard Library.
Given the call bool_element_option_03(arr_value, arr_copy_value); in main and due to array to pointer decay(1), they point to the respective first elements of the two arrays declared in main. They are local variables which happen to have the same name of the variables in main.
Besides, arr_value + ELEMENTS points one after the last element of the array. It's a valid pointer as long as it's not dereferenced.
You can make this function works, without changing the call site, by directly passing those pointers(2) to the SL algorithm functions:
void bool_element_option_03(int const *arr_value, int *arr_copy_value)
{
std::copy(arr_value, arr_value + ELEMENTS, arr_copy_value);
std::sort(arr_copy_value, arr_copy_value + ELEMENTS, std::greater{});
// ...
}
This can be generalized by passing the size too:
void bool_element_option_03( size_t n, int const *src, int *dest)
{ // ^^^^^^^^
std::copy(src, src + n, dest);
std::sort(dest, dest + n, std::greater{});
// ...
}
To generalize a bit more, you can modify the function signature into a template accepting references of array:
template< class Type, std::size_t N>
void bool_element_option_03(Type const (&arr_value)[N], Type (&arr_copy_value)[N])
{
std::copy(std::begin(arr_value), std::end(arr_value),
std::begin(arr_copy_value));
std::sort(std::rbegin(arr_copy_value), std::rend(arr_copy_value));
// ...
}
Which can still be called in main with bool_element_option_03(arr_value, arr_copy_value);, except that, now, there's no decay and the arrays are actually passed by reference and the correct overload of std::begin can be selected.
To mimic the Standard Library algorithms, we can instead pass iterators directly:
template< class SourceRandomIterator, class DestRandomIterator >
void copy_and_sort( SourceRandomIterator source_first
, SourceRandomIterator source_last
, DestRandomIterator dest_first )
{
auto dest_last{ std::copy(source_first, source_last, dest_first) };
std::sort(dest_first, dest_last, std::greater{});
// ...
}
But we also have to modify the call in main:
bool_element_option_03(std::cbegin(arr_value), std::cend(arr_value),
std::begin(arr_copy_value));
Since C++20, we can use std::span as arguments.
void bool_element_option_03(std::span<const int> arr_value,
std::span<int> arr_copy_value)
{
std::copy(std::begin(arr_value), std::end(arr_value),
std::begin(arr_copy_value));
std::sort(std::rbegin(arr_copy_value), std::rend(arr_copy_value));
// ...
}
Or take advantage of the ranges library:
template< std::ranges::random_access_range SourceRange
, std::ranges::random_access_range DestRange >
void bool_element_option_03(SourceRange const& source,
DestRange& dest)
{
std::ranges::copy(source, dest);
std::ranges::sort(dest | std::ranges::views::reverse );
// ...
}
In both cases, this function can be called from main as bool_element_option_03(arr_value, arr_copy_value);
1) What is array to pointer decay?
2) See e.g. How are iterators and pointers related? and how std::sort is declared.
thanks for your help everyone!
this is what I decide to go with.
#include <iostream>
#include <algorithm>
constexpr size_t ELEMENTS{ 8 };
void show(const int(&arr)[ELEMENTS])
{
for (const auto& c : arr)
std::cout << c;
std::cout << '\n';
}
void bool_element_option_03(const int(&arr_value)[ELEMENTS], int(&arr_copy_value)[ELEMENTS])
{
std::copy(std::begin(arr_value), std::end(arr_value), std::begin(arr_copy_value));
std::sort(std::rbegin(arr_copy_value), std::rend(arr_copy_value));
std::cout << "\nReverse sorted:\n";
show(arr_copy_value);
}
int main()
{
const int arr_value[ELEMENTS]{ 1, 2, 9, 4, 5, 6, 7, 8 };
int arr_copy_value[ELEMENTS]{};
std::cout << "\nOriginal order:\n";
show(arr_value);
bool_element_option_03(arr_value, arr_copy_value);
}

Template specification of any kind of array

I'm trying to get rid of void* + size approach to storing arbitrary array types in the same container.
At the moment it looks somewhat like this:
#include <iostream>
#include <map>
#include <string>
#include <cstddef>
struct fat_pointer {
void *data;
size_t size;
size_t count;
fat_pointer() : data(nullptr), size(0), count(0)
{
}
fat_pointer(void *data_, size_t size_, size_t count_) :
data(data_), size(size_), count(count_)
{
}
bool valid() const {
return data != nullptr;
}
template <typename T>
const T as() {
return static_cast<T>(data);
}
};
int main(int argc, char* argv[])
{
// data can be anything, these two are just for example
const double v1[] = {1.1, 2.2, 3.3, 4.4, 5.5};
const int v2[] = {1, 2, 3, 4, 5};
std::map<std::string, fat_pointer> data;
data.insert(std::pair<std::string, fat_pointer>("V1", fat_pointer((void*)v1, sizeof(v1[0]), sizeof(v1) / sizeof(v1[0]))));
data.insert(std::pair<std::string, fat_pointer>("V2", fat_pointer((void*)v2, sizeof(v2[0]), sizeof(v2) / sizeof(v2[0]))));
auto values = data["V1"];
if (values.valid()) {
std::cout << values.as<double*>()[2] << std::endl;
}
return 0;
}
This approach is super error-prone, does not provide any kind of validation, does not allow to easily count or apply algorithms on the elements, so I really want to get rid of it.
Is there some way to tell the compiler that the value will be an array of an arbitrary type? OR is there any other way I can try to avoid the fat_pointer hack?
If you know the types of your arrays up front, you could use std::variant. You can change your fat_pointer struct to hold a variant of type std::pair<T*, T*> where the first and second members of your pair will hold pointers to the start and one past the end of the array respectively. Then have a templated constructor to capture the type of the array.
std::variant will give you the type safety you want. Plus, now that you're storing the begin and end pointers, you can use them with standard algorithms.
If you're going to treat all the arrays is a uniform way, you don't need separate visitor functions in std::visit. You can get type T in std::pair<T*, T*> using:
using T = std::decay_t<decltype(*arg.first)>;
and then use T.
Here's a example:
#include <iostream>
#include <iomanip>
#include <variant>
#include <vector>
#include <iterator>
#include <algorithm>
using PairVariant = std::variant<std::pair<const int*, const int*>, std::pair<const double*, const double*>>;
struct fat_pointer {
PairVariant mPtrs;
template<typename T>
fat_pointer(T* begin, T* end): mPtrs{std::pair<T*, T*>(begin, end)} {}
};
int main()
{
const double v1[] = {1.1, 2.2, 3.3, 4.4, 5.5};
const int v2[] = {1, 2, 3, 4, 5};
fat_pointer ptr1{v1, std::end(v1)};
fat_pointer ptr2{v2, std::end(v2)};
std::vector<fat_pointer> vec{ptr1, ptr2};
for (auto& v: vec) {
std::visit([] (auto&& arg) {
using T = std::decay_t<decltype(*arg.first)>; //type of T in std::pair<T*, T*>
std::copy (arg.first, arg.second, std::ostream_iterator<T>(std::cout, " "));
}, v.mPtrs);
std::cout << std::endl;
}
return 0;
}
Output:
1.1 2.2 3.3 4.4 5.5
1 2 3 4 5
Live demo.
If you don't know the type of the arrays you're going to store in advance, then I think you will have to use std::any. You can store a pointer to the array and its size, and have a template member function to do a std::any_cast. But if there are arrays of many different types, you will end up testing against those types at runtime and that can be pretty ugly!
struct fat_pointer {
std::any mPtr;
std::size_t mSize;
template<typename T>
fat_pointer(T* begin, T* end): mPtr{begin}, mSize(end-begin) {}
template<typename T>
T AnyCast()
{
if (T* ptr = std::any_cast<T>(&mPtr)) {
return *ptr;
}
return nullptr;
}
};
Here's an example.

C2664 cannot convert argument from 'initializer list'

I have the following code that I'm running on Visual Studio 2017. This code is a simple exercise to implement a linear search on an array.
The template is used because the function will be used to any type of array, char array, int array, etc.
#include "stdafx.h"
#include <iostream>
#include <vector>
template <typename T>
int linearSearch(T* arr, int size, T varToSearch) {
for (int i = 0; i < size; i++) {
if (arr[i] == varToSearch) return i;
}
return -1;
}
int main()
{
std::cout << linearSearch({ 'a','b','c','d' }, 4, 'd') << std::endl;
return 0;
}
I get the error of the title and after a long search I did not find the problem.
The microsoft page regarding the error, here, does not have relevant information to understand what is happening.
For me the function should work this way: I have the typename T, that will basically be an int or a char. Let's say it is a char.
When I'm passing {'a','b','c','d'} it will decay into a pointer and, as the type of T is char, I would have following:
int linearSearch(char* arr, int size, char varToSearch)
What for me should work normally.
EDIT
After reading the commentaries and giving a thought about the answers, this is what is happening if you are stuck on this problem also. Let's say you have this syntax in a function:
void exampleFunction(char *text){ \\whatever}
And when using the function you pass this:
exampleFunction({'a', 'b', 'c'}){ \\whatever}
If you are expecting {'a', 'b', 'c'} to decay into a pointer so that you can iterate with text[], it does not. With this syntax you will get an std::initializer_list, and not an array.
You could do the following:
char arr[] = {'a', 'b', 'c'};
exampleFunction(arr){ \\whatever};
This way arr will decay into a pointer.
Regarding the problem in my code, I preferred to use a std::vector.
template <typename T>
int linearSearch(std::vector<T> list, T varToSearch) {
for (typename std::vector<T>::iterator it = list.begin(); it != list.end(); it++) {
if (varToSearch == *it) return (it - list.begin());
}
return -1;
}
Because you can't create array this way. This thing { 'a','b','c','d' } called initializer list, but it doesn't supported operator overload. So this you have 2 solution:
First create array before you called function.
Or you can change function declaration to accepting std::vector by value,and send them initializer list this should works.
And sorry for my engilsh.
as others mentioned you can not do that. you can use the vectors but for some reason if you can't, you can try c arrays or perhaps a better alternative std::array instead.
#include <iostream>
#include <array>
template <typename T, size_t N>
int linearSearch(std::array<T, N> & arr, T varToSearch)
{
int i = 0;
for(auto& element : arr)//iterating through each element
{
if (element == varToSearch)
return i;
++i;
}
return -1;
}
int main()
{
std::array<char, 4> arr1 = {'a','b','c','d'};
std::cout << linearSearch(arr1,'d') << std::endl;
return 0;
}

c++ how to initialize const elements of an array

i need a way to initialize const elements of an array for the program i am currently working on.
The problem is that i have to initialize these elements with a function, there is no way to do it like this:
const int array[255] = {1, 1278632, 188, ...};
because its alot of data i have to generate.
What i tried is to memcpy data to the const int's but that can't work and hasn't worked.
const int array[255];
void generateData(){
for(int i = 0; i < 255; i++) {
initializeSomehowTo(5, array[i]);
}
}
I hope you understand what i am trying, sorry if i doubled the question, i must have overlooked it.
How about this?
#include <array>
typedef std::array<int, 255> Array;
const Array array = generateData();
Array generateData(){
Array a;
for(int i = 0; i < a.size(); i++) {
initializeSomehowTo(a[i]);
}
return a;
}
The easiest approach is to get the filled array from a function and use that to initialize your const (or constexpr) object. However, built-in arrays can't be copied but std::array<T, N> be:
std::array<T, 255> array = initializeData();
If you need a built-in array, I can imagine initializing a static member of a class (template, actually) where the index is expanded from indices expanded from an std::make_index_sequence<255> and used as positional argument in the array, i.e., something along these lines:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <utility>
int some_function(std::size_t i) { return i; }
template <typename> struct initialized_array_base;
template <std::size_t... I>
struct initialized_array_base<std::index_sequence<I...>> {
static const int array[sizeof...(I)];
};
template <std::size_t... I>
int const initialized_array_base<std::index_sequence<I...>>::array[sizeof...(I)]
= { some_function(I)... };
struct initialized_array
:initialized_array_base<std::make_index_sequence<256>> {
};
int main() {
std::copy(std::begin(initialized_array::array),
std::end(initialized_array::array),
std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
}
You can create a writable array, initialize it, and, then, create a const reference to it.
int arry[255];
void generateData(){
for(int i = 0; i < 255; i++) {
initializeSomehowTo(5, arry[i]);
}
}
const int (&array)[255] = arry;

Is it possible to perform a string to int mapping at compile time?

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();
}