I am trying to create (using C++17) a simple debug header that only executes some lines of code if the flag LOGGER_DEBUG_MODE is enabled. This is how my header is defined (I also tried using { x; } instead of x but the warning persists):
debug.h
#ifndef _HDEBUG
#define _HDEBUG
static bool LOGGER_DEBUG_MODE = true;
#define R_DEBUG(x) if(LOGGER_DEBUG_MODE == true) x
#endif
I included debug.h and at some point of my code I call the macro function R_DEBUG to print some values:
logger_adc.cpp
double anlg_device_t::eval_formula()
{
double result = -9999;
try
{
result = parser.Eval();
}
catch (mu::Parser::exception_type &e)
{
std::cout << e.GetMsg() << std::endl;
}
R_DEBUG(std::cout << "Eval Result: " << result << std::endl);
return result;
}
I expected everything to work properly but when I run the makefile I got this warning:
inc/debug.h:5:14: warning: 'LOGGER_DEBUG_MODE' defined but not used [-Wunused-variable]
static bool LOGGER_DEBUG_MODE = true;
I thought that my definition was messed up but after checking the temporary files created by g++, it appears that the preprocessor did everything as I expected:
logger_adc.ii
double anlg_device_t::eval_formula()
{
double result = -9999;
try
{
result = parser.Eval();
}
catch (mu::Parser::exception_type &e)
{
std::cout << e.GetMsg() << std::endl;
}
if(LOGGER_DEBUG_MODE == true) std::cout << "Eval Result: " << result << std::endl;
return result;
}
Why do I get the warning message even when the variable LOGGER_DEBUG_MODE is clearly being used inside the if statement? Did I mess up something obvious that I'm not picking up? My compile flags for the object files (where the warning occurs) are g++ -Wall -Wextra -O1 -g -std=c++17 -save-temps=obj -Iinc -I/usr/local/include -c plus pkg-config --cflags --libs libmodbus
If needed, this is my main function:
main.cpp
#include "logger_adc.h"
int main()
{
anlg_device_t test (ADC_CHIP_1, 1, 18, 1, 1, true);
test.set_formula("2*x","x", test.get_voltage_ptr());
std::cout << "Test Voltage: " << test.get_voltage() << std::endl << "Test Relative: " << test.get_relative() << std::endl;
std::cout << "Test Formula (2*x): " << test.eval_formula() << std::endl;
return 0;
}
Thanks in advance!
You have a header that defines a static bool LOGGER_DEBUG_MODE =true;. If you include that header in multiple C++ files then each file will gets its own copy of that bool.
In your main.cpp you aren't using R_DEBUG so the copy of that bool (which presumably comes from including logger_adc.h ) is indeed unused in that file.
Possible solutions are:
You should make it so you only have a single copy of that bool (declare it in the header with extern and define it in a single C++ file.
Use build defines instead of runtime checks
etc
Related
Consider this short program that I wrote:
#include <iostream>
template<bool Debug = false>
constexpr int add(const int& a, const int& b) {
if (Debug)
std::cout << __FUNCTION__ << " called on line " << __LINE__ << '\n';
return (a + b);
}
int main() {
std::cout << add(3, 7) << '\n';
std::cout << add<true>(5, 9) << '\n';
return 0;
}
It works just fine, and it gives the proper output:
10
add called on line 6
14
However, I'd like for the line number that is printed to be the line at the call site of the program which in this program should be line 12.
So how can I use __LINE__ or some other method to give me the line number from where the function was invoked?
The desired output would be:
10
add called on line 12
14
I would like for it to be generated from the function itself if possible.
-EDIT-
As a note to the reader, I'm open to any and all options but I am limited to C++17 for my current build environment and I'm using Visual Studio.
You can call it like this instead:
template<bool Debug = false>
constexpr int add(const int& a, const int& b, int loc = __LINE__) {
if (Debug)
std::cout << __FUNCTION__ << " called on line " << loc << '\n';
return (a + b);
}
int main() {
std::cout << add(3, 7) << '\n';
std::cout << add<true>(5, 9, __LINE__) << '\n';
return 0;
}
Output:
10
add called on line 14
14
Furthermore, You can define a macro to skip the third argument:
#define add_Debug(a, b) add<true>(a,b,__LINE__)
C++ 20 and beyond
With C++20, we are getting std::source_location which contains information about line number, function, file name. If your compiler supports it, you can use that. Example (tested with g++ 9.3.0). You will not need macros anymore:
#include <iostream>
#include <experimental/source_location>
using source_location = std::experimental::source_location;
template<bool Debug = false>
constexpr int add(const int& a, const int& b, source_location l = source_location::current()) {
if (Debug)
// this will print 'main' as function name
//std::cout << l.function_name() << " called on line " << l.line() << //'\n';
std::cout << source_location::current().function_name() << " called on line " << l.line() << '\n';
return (a + b);
}
int main()
{
std::cout << add(3, 7) << '\n';
std::cout << add<true>(5, 9) << '\n';
return 0;
}
Output:
10
add<true> called on line 16
14
With source_location you don't have to use macros anymore. It will correctly print the line
If you could compile your code on Linux using some recent GCC (in July 2020, that means GCC 10), you might compile with g++ -g -O -Wall then use Ian Taylor's libbacktrace (like we do in RefPerSys) since that library parses DWARF debug information.
You might either port that library to your operating system and debug info format, or find some equivalent.
Today, a cruder possibility could be to wrap some functions with macros. For example, instead of calling foo() with some void foo(void); you would have
extern void foo_at(const char*fileno, int lineno);
#define foo() foo_at(__FILE__,__LINE__);
Indeed, C++20 should add feature tests and source_location but you might need to wait a few months for a compiler supporting it. Consider compiling a recent GCC from its source code. AFAIK MinGW 64 runs on Windows (so ask permission to use it).
void testFunc();
int myval = 0;
int main()
{
int a = 1;
if (a > 0)
{
#define TEST1
testFunc();
#undef TEST1
}
int b = 2;
if ( b > 0)
{
#define TEST2
testFunc();
#undef TEST2
}
std::cout << "val : " << myval << endl;
getchar();
return 0;
}
void testFunc()
{
#ifdef TEST1
#define HERE 1
#endif
#ifdef TEST2
#define HERE 2
#endif
#ifdef HERE
myval = HERE;
#else
myval = -1;
#endif
}
How can I print the value of HERE to be 1 when testFunc() is called first time and then print the value of HERE to be 2 when it is called second time.
With the current code that I have, the value that is being printed is -1.
What you're missing is that the preprocessor directives evaluate before compile time.
That means that as the preprocessor parses your code it:
Sets TEST1
Unsets TEST1
Sets TEST2
Unsets TEST2
Checks if TEST1 is defined (it isn't)
Checks if TEST2 is defined (it isn't)
Checks if HERE is defined (it isn't)
That means testFunc becomes:
void testFunc() {
myval = -1;
}
And then, after this preprocessing, your code is compiled.
I would consider using something other than the preprocessor to accomplish what you are trying to achieve.
The way you intent to do it, it's not possible. Macros are a evaluted before compilation. It will simply parse the document from top to bottom and replace text according to the macros. When the pre-processing reaches testFunc, both TEST1 and TEST2 are not defined anymore (you #undef both earlier in the code), so you end up with
void testFunc()
{
myval = -1;
}
which is then compiled. It looks like you want to create something like a template function? Maybe actual function templates can solve your problem.
How can I print the value of HERE to be 1 when testFunc() is called first time and then print the value of HERE to be 2 when it is called second time.
This is what your attempted function looks like after pre-processing:
void testFunc()
{
myval = -1;
}
As you can see, the function doesn't take any form of input, and always unconditionally assigns the same value to the global variable.
You cannot achieve what you want with macros.
To have the behaviour of printing different value depending on how many times you call a function, you could use a function object instead. Function objects can have internal state:
auto testFunc = [HERE = 1]() mutable {
return HERE++;
};
std::cout << testFunc(); // prints 1
std::cout << testFunc(); // prints 2
All of # statements are called preprocessor directives. These are instructions to the compiler. You want the actually compiled code to maintain some state about the runtime of the program.
Two ways you could accomplish this are either using:
Global variable
Static variable
The following example shows both approaches. Each function also has a memory value which is updated to remember if the function has ever been called.
#include <iostream>
bool g_ran_already = false;
int first_different_global()
{
if (!g_ran_already)
{
g_ran_already = true;
return 1;
}
else
{
return 2;
}
}
int first_different_static()
{
static bool s_ran_already = false;
if(!s_ran_already)
{
s_ran_already = true;
return 1;
}
else
{
return 2;
}
}
int main(int argc, char** argv)
{
std::cout << "first_different_global() run 1: " << first_different_global() << "\n";
std::cout << "first_different_global() run 2: " << first_different_global() << "\n";
std::cout << "first_different_global() run 3: " << first_different_global() << "\n";
std::cout << "first_different_static() run 1: " << first_different_static() << "\n";
std::cout << "first_different_static() run 2: " << first_different_static() << "\n";
std::cout << "first_different_static() run 3: " << first_different_static() << "\n";
}
Output:
first_different_global() run 1: 1
first_different_global() run 2: 2
first_different_global() run 3: 2
first_different_static() run 1: 1
first_different_static() run 2: 2
first_different_static() run 3: 2
I am learning C++ right now and i get this somewhat weird error.
The Code is as follows:
#include <iostream>
#include <array>
using std::cout;
using std::endl;
using std::ostream;
using std::array;
template <typename T, size_t dim>
ostream& operator<<(ostream& os, const array<T,dim>& a) {
os << "[ ";
for (auto n : a)
os << n << " ";
os << "]";
return os;
}
int main()
{
cout << endl << "--- " << __FILE__ << " ---" << endl << endl;
array<int,3> a1 { 2,3,5 }; // (A)
array<int,0> a2 { }; // (B)
array<int,2> a3 { 1 }; // (C)
// array<int> x1 { 1, 2, 3 }; // (D)
// array<int,3> x2 { 1,2,3,4 };
array<int,3> a4 = { 1,2,3 }; // (E)
array<int,3> a5 { { 4,5,6 } }; // (F)
cout << "01| a1=" << a1 << endl;
cout << "02| a2=" << a2 << endl;
cout << "03| a3=" << a3 << endl;
cout << "04| a4=" << a4 << endl;
cout << "05| a5=" << a5 << endl;
cout << endl << "--- " << __FILE__ << " ---" << endl << endl;
return 0;
}
My IDE (Visual Studio Code) shows me the error, although the code is compiling and working.
Here is the makefile provided by our Prof.
# compiler settings
CXX = g++-7
# CXX = clang++
CXXFLAGS = -ansi -pedantic -Wall -Wextra -Wconversion -pthread -std=c++17
LDFLAGS = -lm
# collect files
CXXEXAMPLES = $(shell find . -name '*.cpp' -print -type f)
CXXTARGETS = $(foreach file, $(CXXEXAMPLES), ./out/$(file:.cpp=.out))
# build them all
all: $(CXXTARGETS)
out/%.out: %.cpp
$(CXX) $(CXXFLAGS) $< $(LDFLAGS) -o $#
clean:
rm out/*
I use Ubuntu 16.04 and thought it might be a compiler problem, so I changed "CXX" to "CXX = g++-7", because we were recommended to use g++ Version 7 but it didn't helped.
On typing "g++ -v" it shows that my gcc is version 5.5.0, but typing "apt list -installed" shows that g++-7 is installed.
I did not find any solution on the internet as most similar problems often revolved around missing includes.
VS Code also does not recognize some types of variable definitions like
"int n{1}"
It also complains about the "use of undeclared identifier" on the lines (A) to (E)
I assume the problem lies within the VS Code compiler using a different/old syntax recognition. But I don't know how to change that.
I am trying to debug a problem related to the scope of the character array contained within a std::string. I have posted the relevant code sample below,
#include <iostream>
#include <string>
const char* objtype;
namespace A
{
std::string get_objtype()
{
std::string result;
std::string envstr( ::getenv("CONFIG_STR") );
std::size_t pos1 = 0, pos2 = 0, pos3 = 0;
pos1 = envstr.find_first_of("objtype");
if (pos1 != std::string::npos)
pos2 = envstr.find_first_of("=", pos1+7);
if (pos2 != std::string::npos)
{
pos3 = envstr.find_first_of(";", pos2+1);
if (pos3 != std::string::npos)
result = envstr.substr(pos2+1, pos3 - pos2 - 1);
}
const char* result_cstr = result.c_str();
std::cerr << "get_objtype()" << reinterpret_cast<long>((void*)result_cstr) << std::endl;
return result;
}
void set_objtype()
{
objtype = get_objtype().c_str();
std::cerr << "Objtype " << objtype << std::endl;
std::cerr << "main()" << reinterpret_cast<long>((void*)objtype) << std::endl;
}
}
int main()
{
using namespace A;
std::cerr << "main()" << reinterpret_cast<long>((void*)objtype) << std::endl;
set_objtype();
if (::strcmp(objtype, "AAAA") == 0)
std::cerr << "Do work for objtype == AAAA " << std::endl;
else
std::cerr << "Do work for objtype != AAAA" << std::endl;
}
This was compiled and executed on MacOS 12.3 with g++ 4.2.1. The output from running this is as follows,
$ g++ -g -DNDEBUG -o A.exe A.cpp
$ CONFIG_STR="objtype=AAAA;objid=21" ./A.exe
main()0
get_objtype()140210713147944
Objtype AAAA
main()140210713147944
Do work for objtype == AAAA
$
My questions are these:
The pointer value printed from main() and get_objtype() are the same. Is this due to RVO?
The last line of output shows that the global pointer to C-string is ok even when the enclosing std::string is out of scope. So, when does the returned value go out of scope and the string array deleted? Any help from the community is appreciated. Thanks.
The pointer value won't change, but the memory it points to may no longer be part of a string.
objtype is invalid on the line right after you set it in set_objtype() because the result of get_objtype() isn't saved anywhere, so the compiler is free to kill it there and then.
It may work, but it's accessing invalid memory, so it is invalid code and if you rely on things like this, you will eventually run into big problems.
You should look at the disassembly using objdump to check if its RVO.
But, from experiments I did (making result global and making copies of it), it looks like c_str is reference counted.
Just for fun, I was investigating the order of dynamic initialization of static objects.
In a file name t.h, I put
struct T {
static std::vector<std::string> me;
static int add(std::string s) { me.push_back(s); return me.size(); }
};
(Plus needed headers for vector and string.)
"std::vector T::me" is in t.cpp.
The file main.cpp prints out the values in T::me:
#include "t.h"
#include <iostream>
using namespace std;
int main()
{
T::me.push_back("main");
cout << "T::me.size()=" << T::me.size() << endl;
for (unsigned i = 0; i<T::me.size(); ++i) {
cout << i << "-" << T::me[i] << endl;
}
return 0;
}
next, I create "a.cpp" and put the following in it:
#include "t.h"
int a = T::add("a");
Do the similar for file b.cpp and c.cpp using "b" and "c" as appropriate.
Compile using g++ *.cpp, then run ./a.out. The order of static initialization from compilation unit to compilation unit is unspecified. In my case it is consistently in reverse alphabetical order. I get:
3 - c
2 - b
1 - a
0 - main
No problems so far.
Now I create u.cpp like a.cpp but using "u". Recompile/rerun, "u" does not show up in the list.
Is it because I never referenced u? I never referenced a,b,c, but I change main:
#include "t.h"
#include <iostream>
using namespace std;
extern int u;
int main()
{
cout << "u=" << u << endl;
T::me.push_back("main");
cout << "T::me.size()=" << T::me.size() << endl;
for (unsigned i = 0; i<T::me.size(); ++i) {
cout << i << "-" << T::me[i] << endl;
}
return 0;
}
The program prints out "u=2", but "u" is not in the list. Shouldn't u have been dynamically initialized before use and, therefore, T::me have been updated to include "u"? I think there should be a better explanation than that u comes after t in the alphabet.
I've got it. Simple, really.
T::me is zero initialized statically according to the rules of C++. There is a constructor, however, to run dynamically. There's no guarantee when that constructor runs. It is apparently - in this case - running after u is initialized.
It seems that the compilation linking order matters:
g++ -o m a.cpp u.cpp t.cpp main.cpp
gives
a=2
u=1
T::me.size()=3
0-u
1-a
2-main
but
g++ -o m main.cpp t.cpp a.cpp u.cpp
gives
a=2
u=1
T::me.size()=1
0-main
and reversing a.cpp and u.cpp in the last case causes a=1 and u=2.
Interesting!