3

I have to use a macro PERF_INSTRUMENT from a library. PERF_INSTRUMENT expects a user provided c-style string as a function name to print the location of this instrument point.

But, I don't want to write the function name everytime I use PERF_INSTRUMENT instead I want to call it with __func__ so that function name is automatically included in the perf log.

But when I use __func__ it actually returns operator() because the __func__ is embedded inside the lambda function.

Is their any way by which I can pass the main() function name to the PERF_INSTRUMENT macro.

#include <cstdio>
#include <cassert> 
#include <type_traits> 

using namespace std;

namespace /* anonymous */
{
    template< typename T >
    struct Is_Const_Char_Array
      : std::is_same< std::remove_reference_t< T >,
                      char const[ std::extent< std::remove_reference_t< T > >::value ] >
    {};

    template< typename T >
    struct Is_C_String_Literal
      : Is_Const_Char_Array< T >
    {};
}

#define PERF_INSTRUMENT(name)  auto instObj = [] { static_assert( Is_C_String_Literal< decltype( name ) >::value, "input argument must be a c-string literal" ); /* Some other Logic*/ printf(name);return 1; }()


// <------------------ MY CODE -------------------> //

int main(){
    PERF_INSTRUMENT("main"); // <-- this works fine
    PERF_INSTRUMENT(__func__); // <-- this prints operator()
    // PERF_INSTRUMENT(__builtin_FUNCTION());
}

Please Note that I can only change the code below the MY CODE line

Mohit
  • 1,859
  • 1
  • 16
  • 25

2 Answers2

5

Is their any way by which I can pass the main function name to the PERF_INSTRUMENT macro.

You can pass "name" as argument to the lambda itself.

Something as

#define PERF_INSTRUMENT(name) \
    auto instObj = [](char const * str) \ // <-- receive an argument
       { static_assert( Is_C_String_Literal< decltype( name ) >::value, \
                       "input argument must be a c-string literal" );\
         /* Some other Logic*/ \
         printf(str); \  // <-- print the argument received, not directly name
         return 1;\
       }(name)
//.......^^^^   pass name as argument

Bonus Off Topic proposal: to detect is an object is a C-string literal, i propose an alternative way

template <typename T>
constexpr std::false_type islHelper (T, long);

template <typename T, std::size_t N>
constexpr std::true_type islHelper (T const(&)[N], int);

template <typename T>
using isStringLiteral = decltype(islHelper(std::declval<T>(), 0));

In static_assert() become

static_assert( isStringLiteral<decltype(name)>::value,
               "input argument must be a c-string literal" );
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
max66
  • 65,235
  • 10
  • 71
  • 111
  • 1
    That assert isn't very robust. For example, `const char s[1]; static_assert(isStringLiteral::value, "You won't see this message." );` – molbdnilo Nov 21 '18 at 14:03
  • @molbdnilo - Unfortunately you're right. I can solve this problem imposing (through SFINAE) that `N` is greater than `1` but I don't know if is the case: `char const s[] { "abc" };` result a string literal but this isn't exactly correct (I suppose). – max66 Nov 21 '18 at 18:38
  • The size has nothing to do with it, and your `s` is not a string literal. There is no way to distinguish between string literals and other `const` char arrays. – molbdnilo Nov 21 '18 at 18:53
  • Pls understand that I cannot change the `PERF_INSTRUMENT` macro – Mohit Nov 26 '18 at 09:06
0

Since the assert is fundamentally flawed – it accepts any const char array – wrapping the macro in another macro should work.
Something like this:

#define PERF_FUNCTION do { \
    const char name[] = __func__; \
    PERF_INSTRUMENT(name); \
} while(0)
molbdnilo
  • 64,751
  • 3
  • 43
  • 82