Stringize operator on argument with spaces - c++

I have
#define ARG(TEXT , REPLACEMENT) replace(#TEXT, REPLACEMENT)
so
QString str= QString("%ONE, %TWO").ARG(%ONE, "1").ARG(%TWO, "2");
becomes
str= QString("%ONE, %TWO").replace("%ONE", "1").replace("%TWO", "2");
//str = "1, 2"
The problem is that VS2019, when formatting the code (Edit.FormatSelection) interprets that % sign as an operator and adds a whitespace
QString str= QString("%ONE, %TWO").ARG(% ONE, "1").ARG(% TWO, "2");
(I think it's a bug in VS). The code compiles without warnings.
As I am dealing with some ancient code that has this "feature" spread, I'm worried to auto-format text containing this and break functionality.
Is there a way at compile time to detect such arguments to a macro having space(s)?

Is there a way at compile time to detect such arguments to a macro having space(s)?
Here's what I would do:
#define ARG(TEXT, REPLACEMENT) \
replace([]{ \
static constexpr char x[] = #TEXT; \
static_assert(x[0] == '%' && x[1] != ' '); \
return x; \
}(), REPLACEMENT)

Apparently some time in the next decade C++ will provide a better solution, and indeed there might be a much less clunky solution than the one I provide below, but it's maybe a place to start.
This version uses the Boost Preprocessor library to do a repetition which would have been straight-forward to write with a template if C++ allowed string literals as template arguments, a feature which has not yet gotten into the standard for motivations I can only guess at. So it doesn't actually test whether the argument has no spaces; rather it tests that there are no spaces in the first 64 characters (where 64 is an almost entirely arbitrary number which can be changed as your needs dictate). I used the Boost Preprocessor library; you could do this with your own special purpose macros if for some reason you don't want to use Boost.
#include <boost/preprocessor/repetition/repeat.hpp>
#define NO_SPACE_AT_N(z, N, s) && (N >= sizeof(s) || s[N] != ' ')
#define NO_SPACE(s) true BOOST_PP_REPEAT(64, NO_SPACE_AT_N, s)
// Arbitrary constant, change as needed---^
// Produce a compile time error if there's a space.
template<bool ok> struct NoSpace {
const char* operator()(const char* s) {
static_assert(ok, "Unexpected space");
return s;
}
};
#define ARG(TEXT, REPL) replace(NoSpace<NO_SPACE(#TEXT)>()(#TEXT), REPL)
(Test on gcc.godbolt.)

If the question is to produce a compilation error when the first argument of ARG contains a space, I managed to get this to work:
#include <cstdlib>
template<size_t N>
constexpr int string_validate( const char (&s)[N] )
{
for (int i = 0; i < N; ++i)
if ( s[i] == ' ' )
return 0;
return 1;
}
template<int N> void assert_const() { static_assert(N, "string validation failed"); }
int replace(char const *, char const *) { return 0; } // dummy for example
#define ARG(TEXT , REPLACEMENT) replace((assert_const<string_validate(#TEXT)>(), #TEXT), REPLACEMENT)
int main()
{
auto b = ARG(%TWO, "2");
auto a = ARG(% ONE, "1"); // causes assertion failure
}
Undoubtedly there is a shorter way. Prior to C++20 you can't use a string literal in a template parameter, hence the constexpr function to produce an integer from the string literal and then we can check the integer at compile-time by using it as a template parameter.

It's unlikely.
Visual Studio works on source code, without running the preprocessor first and without performing what would be quite a difficult computation to work out whether the preprocessor would fundamentally alter the line it's formatting.
Besides, people don't really use macros in this way any more, or shouldn't (we have cheap functions!).
So this isn't really what the formatting feature expects.
If you can modify the code, make the user write .ARG("%ONE", "1"), then not only does the problem go away but also the would be more consistent.
Otherwise, you'll have to stick with formatting the code by hand.

Related

What is the C++ convention when I need to add a useless return statement?

I was trying to write a function that returns the first non-repeated character in a string. The algorithm I made was:
Assert that the string is non-empty
Iterate through the string and add all non-repeated characters to a set
Assert that the set be non-empty
Iterate through string again and return the first character that's in the set
Add a useless return statement to make the compiler happy. (Arbitrarily return 'F')
Obviously my algorithm is very "brute force" and could be improved on. It runs, anyhow. I was wondering if there's a better way to do this and was also wondering what the convention is for useless return statements. Don't be afraid to criticize me harshly. I'm trying to become a C++ stiffler. ;)
#include <iostream>
#include <string>
#include <set>
char first_nonrepeating_char(const std::string&);
int main() {
std::string S = "yodawgIheardyoulike";
std::cout << first_nonrepeating_char(S);
}
// Finds that first non-repeated character in the string
char first_nonrepeating_char(const std::string& str) {
assert (str.size() > 0);
std::set<char> nonRepChars;
std::string::const_iterator it = str.begin();
while (it != str.end()) {
if (nonRepChars.count(*it) == 0) {
nonRepChars.insert(*it);
} else {
nonRepChars.erase(*it);
}
++it;
}
assert (nonRepChars.size() != 0);
it = str.begin();
while (it != str.end()) {
if (nonRepChars.count(*it) == 1) return (*it);
++it;
}
return ('F'); // NEVER HAPPENS
}
The main problem is just getting rid of warnings.
Ideally you should be able to just say
assert( false ); // Should never get here
but unfortunately that does not get rid of all warnings with the compilers I use most, namely Visual C++ and g++.
Instead I do this:
xassert_should_never_get_here();
where xassert_should_never_get_here is a function that
is declared as "noreturn" by compiler-specific means, e.g. __declspec for Visual C++,
has an assert(false) to handle debug builds,
then throws a std::logic_error.
The last two points are accomplished by a macro XASSERT (its actual name in my code is CPPX_XASSERT, it's always a good idea to use prefixes for macro names so as to reduce name conflict probability).
Of course, the assertion that you should not get to the end, is equivalent to an assertion that the argument string does contain at least one non-repeated character, which therefore is a precondition of the function (part of its contract), which I think should be documented by a comment. :-)
There are three main "modern C++" ways of coding things up when you do not have that precondition, namely
choose one char value to signify "no such", e.g. '\0', or
throw an exception in the case of no such, or
return a boxed result which can be logically "empty", e.g. the Boost class corresponding to Barton and Nackmann's Fallible.
About the algorithm: when you're not intested in where the first non-repeating char is, you can avoid the rescan of the string by maintaining a count per character, e.g. by using a map<char, int> instead of a set<char>.
There is a simpler and "cleaner" way of doing it, but it is not computationally faster than "brute force".
Use a table that counts the number of occurrences of each character in the input string.
Then go over the input string one more time, and return the first character whose count is 1.
char GetFirstNonRepeatedChar(const char* s)
{
int table[256] = {0};
for (int i=0; s[i]!=0; i++)
table[s[i]]++;
for (int i=0; s[i]!=0; i++)
if (table[s[i]] == 1)
return s[i];
return 0;
}
Note: the above will work for ASCII strings.
If you're using a different format, then you'll need to change the 256 (and the char of course).

Can a compilation error be forced if a string argument is not a string literal?

Let's say I have these two overloads:
void Log(const wchar_t* message)
{
// Do something
}
void Log(const std::wstring& message)
{
// Do something
}
Can I then in the first function add some compile-time verifiction that the passed argument is a string literal?
EDIT: A clarification on why this would be good in my case; my current high-frequency logging uses only string literals and can hence be optimized a lot when there are non-heap allocation guarantees. The second overload doesn't exist today, but I might want to add it, but then I want to keep the first one for extreme scenarios. :)
So this grew out of Keith Thompson's answer... As far as I know, you can't restrict string literals to only normal functions, but you can do it to macro functions (through a trick).
#include <iostream>
#define LOG(arg) Log(L"" arg)
void Log(const wchar_t *message) {
std::wcout << "Log: " << message << "\n";
}
int main() {
const wchar_t *s = L"Not this message";
LOG(L"hello world"); // works
LOG(s); // terrible looking compiler error
}
Basically, a compiler will convert "abc" "def" to look exactly like "abcdef". And likewise, it will convert "" "abc" to "abc". You can use this to your benefit in this case.
I also saw this comment on the C++ Lounge, and that gave me another idea of how to do this, which gives a cleaner error message:
#define LOG(arg) do { static_assert(true, arg); Log(arg); } while (false)
Here, we use the fact that static_assert requires a string literal as it's second argument. The error that we get if we pass a variable instead is quite nice as well:
foo.cc:12:9: error: expected string literal
LOG(s);
^
foo.cc:3:43: note: expanded from macro 'LOG'
#define LOG(arg) do { static_assert(true, arg); Log(arg); } while (false)
I believe the answer to your question is no -- but here's a way to do something similar.
Define a macro, and use the # "stringification" operator to guarantee that only a string literal will be passed to the function (unless somebody bypasses the macro and calls the function directly). For example:
#include <iostream>
#define LOG(arg) Log(#arg)
void Log(const char *message) {
std::cout << "Log: " << message << "\n";
}
int main() {
const char *s = "Not this message";
LOG("hello world");
LOG(hello world);
LOG(s);
}
The output is:
Log: "hello world"
Log: hello world
Log: s
The attempt to pass s to LOG() did not trigger a compile-time diagnostic, but it didn't pass that pointer to the Log function.
There are at least two disadvantages to this approach.
One is that it's easily bypassed; you may be able to avoid that by searching the source code for references to the actual function name.
The other is that stringifying a string literal doesn't just give you the same string literal; the stringified version of "hello, world" is "\"hello, world\"". I suppose your Log function could strip out any " characters in the passed string. You may also want to handle backslash escapes; for example, "\n" (a 1-character string containing a newline) is stringified as "\\n" (a 2-character string containing a backslash and the letter n).
But I think a better approach is not to rely on the compiler to diagnose calls with arguments other than string literals. Just use some other tool to scan the source code for calls to your Log function and report any calls where the first argument isn't a string literal. If you can enforce a particular layout for the calls (for example, the tokens Log, (, and a string literal on the same line), that shouldn't be too difficult.
You can't detect string literals directly but you can detect if the argument is an array of characters which is pretty close. However, you can't do it from the inside, you need to do it from the outside:
template <std::size_t Size>
void Log(wchar_t const (&message)[Size]) {
// the message is probably a string literal
Log(static_cast<wchar_t const*>(message);
}
The above function will take care of wide string literals and arrays of wide characters:
Log(L"literal as demanded");
wchar_t non_literal[] = { "this is not a literal" };
Log(non_literal); // will still call the array version
Note that the information about the string being a literal isn't as useful as one might hope for. I frequently think that the information could be used to avoid computing the string length but, unfortunately, string literals can still embed null characters which messes up static deduction of the string length.
If you define Log as a macro instead, and call separate methods for literal versus std::wstring handling, some variation of the following should work:
#define Log(x) ((0[#x] == 'L' && 1[#x] == '"') ? LogLiteral(x) : LogString(x))
void
LogLiteral (const wchar_t *s) {
//...do something
}
void
LogString (const std::wstring& s) {
//...do something
}
The trick is that you need opposing definitions of LogLiteral() so that the compilation will pass, but it should never be called.
inline void LogLiteral (const std::wstring &s) {
throw std::invalid_argument(__func__);
}
This code gives you the behavior of an overloaded Log() method, in that you can pass either a string literal or a non-string literal to the Log() macro, and it will end up calling either LogLiteral() or LogString(). This gives compile time verification in that the compiler will not pass anything except what the code recognizes as a string literal to the call to LogLiteral(). At sufficient optimizations, the conditional branch can be removed, since every instance of the check is static (on GCC, it is removed).
Here's a quick example I just whipped up using the printf hack I suggested in the comments above:
#include <cstdio>
#define LOG_MACRO(x) do { if (0) printf(x); Log(x); } while (0)
void Log(const char *message)
{
// do something
}
void function(void)
{
const char *s = "foo";
LOG_MACRO(s);
LOG_MACRO("bar");
}
Output from compiling this one with Clang appears to be exactly what you're looking for:
$ clang++ -c -o example.o example.cpp
example.cpp:13:15: warning: format string is not a string literal
(potentially insecure) [-Wformat-security]
LOG_MACRO(s);
^
example.cpp:3:41: note: expanded from macro 'LOG_MACRO'
#define LOG_MACRO(x) do { if (0) printf(x); Log(x); } while (0)
^
1 warning generated.
I did have to switch to printf rather than wprintf, since the latter appears not to generate the warning - I guess that's probably a Clang bug, though.
GCC's output is similar:
$ g++ -c -o example.o example.cpp
example.cpp: In function ‘void function()’:
example.cpp:13: warning: format not a string literal and no format arguments
example.cpp:13: warning: format not a string literal and no format arguments
Edit: You can see the Clang bug here. I just added a comment about -Wformat-security.
I don't think you can enforce to pass only a string literal to a function, but literals are character arrays, what you can enforce:
#include <iostream>
template<typename T>
void log(T) = delete; //Disable everything
template <std::size_t Size>
void log(const wchar_t (&message)[Size]) //... but const wchar_t arrays
{
std::cout << "yay" << std::endl;
}
const wchar_t * get_str() { return L"meow"; }
int main() {
log(L"foo"); //OK
wchar_t arr[] = { 'b', 'a', 'r', '0' };
log(arr); //Meh..
// log(get_str()); //compile error
}
Downside is that if you have a runtime character array, it will work as well, but won't work for the usual runtime c-style strings.
But, if you can work with a slightly different syntax, then the answer is YES:
#include <cstddef>
#include <iostream>
void operator"" _log ( const wchar_t* str, size_t size ) {
std::cout << "yay" << std::endl;
}
int main() {
L"Message"_log;
}
Of course, both solution needs a C++11-compatible compiler (example tested with G++ 4.7.3).
Adding this alternative for future reference. It comes from the SO question Is it possible to overload a function that can tell a fixed array from a pointer?
#include <iostream>
#include <type_traits>
template<typename T>
std::enable_if_t<std::is_pointer<T>::value>
foo(T)
{
std::cout << "pointer\n";
}
template<typename T, unsigned sz>
void foo(T(&)[sz])
{
std::cout << "array\n";
}
int main()
{
char const* c = nullptr;
char d[] = "qwerty";
foo(c);
foo(d);
foo("hello");
}
The above snippet compiles and runs fine on http://webcompiler.cloudapp.net/

C++ integer template parameter evaluation

This question may be very straightforward but I am rather inexperienced with c++ and got stuck while writing a simple parser.
For some reason one of the string comparison functions would not return the expected value when called.
The function looks like this:
template<int length>
bool Parser::compare(const char *begin, const char *str){
int i = 0;
while(i != length && compareCaseInsensitive(*begin, *str)){
i++;
begin++;
str++;
}
return i == length;
};
The purpose of this function was to compare a runtime character buffer with a compile time constant string vb
compare<4>(currentByte, "<!--");
I know there are more efficient ways to compare a fixed length character buffer (and used one later on) but I was rather puzzled when I ran this function and it always returns false, even with two identical strings.
I checked with the debugger and checked the value of i at the end of the loop and it was equal to the value of the template parameter but still the return expression evaluated to false.
Are there any special rules about working with int template parameters ?
I assumed the template parameter would behave like a compile time constant.
I don't know if this is relevant but I'm running gcc's g++ compiler and debugged with gdb.
If anyone could tell me what might cause this problem it would be highly appreciated.
The functions used in this piece of code:
template<typename Character>
Character toLowerCase(Character c){
return c > 64 && c < 91 ? c | 0x10 : c;
};
template<typename Character>
bool equalsCaseInsensitive(Character a, Character b){
return toLowerCase(a) == toLowerCase(b);
};
For doing case-insensitive string comparisons, I would try using the STL function std::strcoll from the header <cstring> which has signature
int strcoll( const char* lhs, const char* rhs );
and compares two null-terminated byte strings according to the current locale. Or if you want to roll your own, you could still use std::tolower from the header <cctype> which has signature
int tolower( int ch );
and converts the given character to lowercase according to the character conversion rules defined by the currently installed C locale.

Macro to count (character) arguments

I have a macro to convert a string to a list of characters:
#define TO_STRING(x) #x
#define CHAR_LIST_7(x) TO_STRING(x)[0] \
, TO_STRING(x)[1] \
, TO_STRING(x)[2] \
, TO_STRING(x)[3] \
, TO_STRING(x)[4] \
, TO_STRING(x)[5] \
, TO_STRING(x)[6]
e.g. usage:
"CHAR_LIST_7(chicken)" gives "'c', 'h', 'i', 'c', 'k', 'e', 'n'" so it can be used in things like templates (e.g.: http://hpaste.org/47313/exand )
However, I would like to generalize this for any amount of characters (and not need to manually have to count the amount of characters)? So I could simply go: CHAR_LIST(arbitrary text). Any ideas or solutions ?
You can't split tokens during preprocessing, you can only combine them (using ##).
Converting the identifier to a string literal won't help either as you can't string split a string literal apart during preprocessing, nor can you perform operations (e.g. compute length) on a string literal.
During preprocessing the compiler knows that a token is a string literal and what kind of literal it is, but it does not yet know its full type and length, at least not in a way that is accessible to a macro.
No, as James already said, there is no way to split tokens in the preprocessor or to have it to know the length of a string.
But I think that for your use case this is not necessary at all in any case. A string that you would get from stringifying your argument with #x is a constant sized string, e.g chicken leads to "chicken" which simply has type char[8]. The length of such a string is a compile time constant and you simply can detect it with sizeof:
#define TOKLEN(TOK) (sizeof(#TOK)-1)
Usage of such a thing in C would "simply" look
#define SCARY(TOK) for (size_t i = 0; i < TOKLEN(TOK); ++i) printf("%c:", #TOK[i])
Because TOKLEN(TOK) is a compile time constant the compiler could unroll this if appropriate.
To use that in your use case for C++
template < size_t n >
class constLenString {
size_t const len = n;
char const* str;
constLenString(char* s) : str(s) { }
};
#define defConstLenString(TOK, NAME) constLenString< TOKLEN(TOK) > NAME(#TOK)
(untested, my C++ is rusty)
and now with
defConstLenString(chicken, chick);
chick.n is a constant that can be the bound of a for loop or whatever and the compiler should be able to optimize everything perfectly.
The way you ask for splitting is not possible in general form. The best way is to provide the argument as it's to the template:
Literal<'c','h','i','c','k','e','n'>::print();
I know that it will be a bit more of typing. To overcome that you can write a simple program, which takes a string literal like chicken as argument and outputs it into 'c','h','i',...etc. something like:
int main(int argc, char **argv)
{
if(argc != 2)
return 0;
const char *s = argv[1];
int length = strlen(s)-1;
for(int i = 0; i < length; i++)
cout<<s[i]<<",";
cout<<s[length]<<endl;
}
However, there is no solution for verbosity due to split-ted characters in your template argument.

__FILE__ macro manipulation handling at compile time

One of the issues I have had in porting some stuff from Solaris to Linux is that the Solaris compiler expands the macro __FILE__ during preprocessing to the file name (e.g. MyFile.cpp) whereas gcc on Linux expandeds out to the full path (e.g. /home/user/MyFile.cpp). This can be reasonably easily resolved using basename() but....if you're using it a lot, then all those calls to basename() have got to add up, right?
Here's the question. Is there a way using templates and static metaprogramming, to run basename() or similar at compile time? Since __FILE__ is constant and known at compile time this might make it easier. What do you think? Can it be done?
In projects using CMake to drive the build process, you can use a macro like this to implement a portable version that works on any compiler or platform. Though personally I pity the fool who must use something other than gcc... :)
# Helper function to add preprocesor definition of FILE_BASENAME
# to pass the filename without directory path for debugging use.
#
# Note that in header files this is not consistent with
# __FILE__ and __LINE__ since FILE_BASENAME will be the
# compilation unit source file name (.c/.cpp).
#
# Example:
#
# define_file_basename_for_sources(my_target)
#
# Will add -DFILE_BASENAME="filename" for each source file depended on
# by my_target, where filename is the name of the file.
#
function(define_file_basename_for_sources targetname)
get_target_property(source_files "${targetname}" SOURCES)
foreach(sourcefile ${source_files})
# Add the FILE_BASENAME=filename compile definition to the list.
get_filename_component(basename "${sourcefile}" NAME)
# Set the updated compile definitions on the source file.
set_property(
SOURCE "${sourcefile}" APPEND
PROPERTY COMPILE_DEFINITIONS "FILE_BASENAME=\"${basename}\"")
endforeach()
endfunction()
Then to use the macro, just call it with the name of the CMake target:
define_file_basename_for_sources(myapplication)
Using C++11, you have a couple of options. Let's first define:
constexpr int32_t basename_index (const char * const path, const int32_t index = 0, const int32_t slash_index = -1)
{
return path [index]
? ( path [index] == '/'
? basename_index (path, index + 1, index)
: basename_index (path, index + 1, slash_index)
)
: (slash_index + 1)
;
}
If your compiler supports statement expressions, and you want to be sure that the basename computation is being done at compile-time, you can do this:
// stmt-expr version
#define STRINGIZE_DETAIL(x) #x
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
#define __FILELINE__ ({ static const int32_t basename_idx = basename_index(__FILE__);\
static_assert (basename_idx >= 0, "compile-time basename"); \
__FILE__ ":" STRINGIZE(__LINE__) ": " + basename_idx;})
If your compiler doesn't support statement expressions, you can use this version:
// non stmt-expr version
#define __FILELINE__ (__FILE__ ":" STRINGIZE(__LINE__) ": " + basename_index(__FILE__))
With this non stmt-expr version, gcc 4.7 and 4.8 call basename_index at run-time, so you're better off using the stmt-expr version with gcc. ICC 14 produces optimal code for both versions. ICC13 can't compile the stmt-expr version, and produces suboptimal code for the non stmt-expr version.
Just for completeness, here's the code all in one place:
#include <iostream>
#include <stdint.h>
constexpr int32_t basename_index (const char * const path, const int32_t index = 0, const int32_t slash_index = -1)
{
return path [index]
? ( path [index] == '/'
? basename_index (path, index + 1, index)
: basename_index (path, index + 1, slash_index)
)
: (slash_index + 1)
;
}
#define STRINGIZE_DETAIL(x) #x
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
#define __FILELINE__ ({ static const int32_t basename_idx = basename_index(__FILE__); \
static_assert (basename_idx >= 0, "compile-time basename"); \
__FILE__ ":" STRINGIZE(__LINE__) ": " + basename_idx;})
int main() {
std::cout << __FILELINE__ << "It works" << std::endl;
}
There is currently no way of doing full string processing at compile time (the maximum we can work with in templates are the weird four-character-literals).
Why not simply save the processed name statically, e.g.:
namespace
{
const std::string& thisFile()
{
static const std::string s(prepocessFileName(__FILE__));
return s;
}
}
This way you are only doing the work once per file. Of course you can also wrap this into a macro etc.
you might want to try the __BASE_FILE__ macro. This page describes a lot of macros which gcc supports.
Another C++11 constexpr method is as follows:
constexpr const char * const strend(const char * const str) {
return *str ? strend(str + 1) : str;
}
constexpr const char * const fromlastslash(const char * const start, const char * const end) {
return (end >= start && *end != '/' && *end != '\\') ? fromlastslash(start, end - 1) : (end + 1);
}
constexpr const char * const pathlast(const char * const path) {
return fromlastslash(path, strend(path));
}
The usage is pretty simple also:
std::cout << pathlast(__FILE__) << "\n";
The constexpr will be performed at compile-time if possible, otherwise it will fallback to run-time execution of the statements.
The algorithm is a little different in that it finds the end of the string and then works backwards to find the last slash. It is probably slower than the other answer but since it is intended to be executed at compile-time it shouldn't be an issue.
I like #Chetan Reddy's answer, which suggests using static_assert() in a statement expression to force a compile time call to function finding the last slash, thus avoiding runtime overhead.
However, statement expressions are a non-standard extension and are not universally supported. For instance, I was unable to compile the code from that answer under Visual Studio 2017 (MSVC++ 14.1, I believe).
Instead, why not use a template with integer parameter, such as:
template <int Value>
struct require_at_compile_time
{
static constexpr const int value = Value;
};
Having defined such a template, we can use it with basename_index() function from #Chetan Reddy's answer:
require_at_compile_time<basename_index(__FILE__)>::value
This ensures that basename_index(__FILE__) will in fact be called at compile time, since that's when the template argument must be known.
With this, the complete code for, let's call it JUST_FILENAME, macro, evaluating to just the filename component of __FILE__ would look like this:
constexpr int32_t basename_index (
const char * const path, const int32_t index = 0, const int32_t slash_index = -1
)
{
return path [index]
? ((path[index] == '/' || path[index] == '\\') // (see below)
? basename_index (path, index + 1, index)
: basename_index (path, index + 1, slash_index)
)
: (slash_index + 1)
;
}
template <int32_t Value>
struct require_at_compile_time
{
static constexpr const int32_t value = Value;
};
#define JUST_FILENAME (__FILE__ + require_at_compile_time<basename_index(__FILE__)>::value)
I've stolen basename_index() almost verbatim from the previously mentioned answer, except I added a check for Windows-specific backslash separator.
Another possible approach when using CMake is to add a custom preprocessor definition that directly uses make's automatic variables (at the cost of some arguably ugly escaping):
add_definitions(-D__FILENAME__=\\"$\(<F\)\\")
Or, if you're using CMake >= 2.6.0:
cmake_policy(PUSH)
cmake_policy(SET CMP0005 OLD) # Temporarily disable new-style escaping.
add_definitions(-D__FILENAME__=\\"$\(<F\)\\")
cmake_policy(POP)
(Otherwise CMake will over-escape things.)
Here, we take advantage of the fact make substitutes $(<F) with the source file name without leading components and this should show up as -D__FILENAME__=\"MyFile.cpp\" in the executed compiler command.
(While make's documentation recommends using $(notdir path $<) instead, not having whitespace in the added definition seems to please CMake better.)
You can then use __FILENAME__ in your source code like you'd use __FILE__. For compatibility purposes you may want to add a safe fallback:
#ifndef __FILENAME__
#define __FILENAME__ __FILE__
#endif
For Objective-C the following macro provides a CString which can replace the __FILE__ macro, but omitting the initial path components.
#define __BASENAME__ [[[NSString stringWithCString:__FILE__ \
encoding:NSUTF8StringEncoding] \
lastPathComponent] \
cStringUsingEncoding:NSUTF8StringEncoding]
That is to say it converts: /path/to/source/sourcefile.m into: sourcefile.m
It works by taking the ouptput of the __FILE__ macro (which is a C-formatted, null terminated string), converting it to an Objective-C string object, then stripping out the initial path components and finally converting it back into a C formatted string.
This is useful to get logging format which is more readable, replacing, (for example) a logging macro like this:
#define MyLog(fmt, ...) MyLog((#"E %s [Line %d] " fmt), \
__FILE__, __LINE__, ##__VA_ARGS__)
with:
#define __BASENAME__ [[[NSString stringWithCString:__FILE__ \
encoding:NSUTF8StringEncoding] \
lastPathComponent] \
cStringUsingEncoding:NSUTF8StringEncoding]
#define MyLog(fmt, ...) MyLog((#"E %s [Line %d] " fmt), \
__BASENAME__, __LINE__, ##__VA_ARGS__)
It does contain some runtime elements, and in that sense does not fully comply with the question, but it is probably appropriate for most circumstances.
I've compressed the constexpr version down to one recursive function that finds the last slash and returns a pointer to the character after the slash. Compile time fun.
constexpr const char* const fileFromPath(const char* const str, const char* const lastslash = nullptr) {
return *str ? fileFromPath(str + 1, ((*str == '/' || *str == '\\') ? str + 1 : (nullptr==lastslash?str:lastslash)) : (nullptr==lastslash?str:lastslash);
}