Strange PC-Lint errors with member templates - c++

I am currently struggling with PC-Lint (version 9.00j and l), which gives me some errors and warnings for a piece of code. The code compiles well and runs as expected. Here is a simplified version of it:
#include <iostream>
#include <vector>
typedef unsigned char uint8_t;
class Test
{
uint8_t inputList[10];
std::vector<int> resultList;
public:
Test() : resultList()
{
for (uint8_t ii = 0; ii < 10; ++ii)
inputList[ii] = ii;
}
template<int list_size, typename ResultListType>
void loadList(const uint8_t (& inputList)[list_size],
ResultListType & resultList) const
{
for (uint8_t ii = 0; ii < list_size; ++ii)
resultList.push_back(inputList[ii]);
}
void run()
{
loadList(inputList, resultList);
}
void print()
{
std::vector<int>::iterator it;
for (it = resultList.begin(); it != resultList.end(); ++it)
std::cout << *it << std::endl;
}
};
int main()
{
Test t;
t.run();
t.print();
}
When running this in Gimpel's online demo, I get these errors and warnings:
30 loadList(inputList, resultList);
diy.cpp 30 Error 1025: No template matches invocation 'Test::loadList(unsigned char [10], std::vector<int>)', 1 candidates found, 1 matched the argument count
diy.cpp 30 Info 1703: Function 'Test::loadList(const unsigned char (&)[V], <2>&) const' arbitrarily selected. Refer to Error 1025
diy.cpp 30 Error 1032: Member 'loadList' cannot be called without object
diy.cpp 30 Error 1058: While calling 'Test::loadList(const unsigned char (&)[V], <2>&) const': Initializing a non-const reference '<2>&' with a non-lvalue (a temporary object of type 'std::vector<int>')
diy.cpp 30 Warning 1514: Creating temporary to copy 'std::vector<int>' to '<2>&' (context: arg. no. 2)
So basically, PC-Lint is trying to tell me that it will just find the right template parameters by chance and that only a temporary copy of the vector will be filled. But the code runs well, the resultList contains the data!
Can anyone tell me what is going on here? Is PC-Lint right and something is going wrong or is this just a PC-Lint bug?

The problem is that loadList is marked to be const, and yet you pass a non-constant reference to the member variable resultList that you modify.
It's true that the loadList function doesn't modify this instance directly but since you still modify a member variable the function can't be constant.
Either create a temporary vector that you pass to the function, or make the function not const.

Related

Modifying a value in a boost::json::object

I've been trying to use a boost::json::object to store a JSON object with several values and then modify values as needed, but have been unable to figure out a way to perform the modifications, even after reviewing information on C++ references and overloaded methods differing only by return type which seemed to be pertinent.
In the following test code, I create a brief JSON object in a boost::json::object as illustrated in the "Quick Look" section of the Boost.JSON documentation, read out and display a value from it, attempt to modify that value, and then read out and display the new value.
#include <iostream>
#include <boost/json/src.hpp>
#include <boost/json/object.hpp>
using std::cout;
using std::endl;
int main(int argc, char* argv[]) {
boost::json::object testobj;
testobj["properties"] = { { "timestamp", "Fri Jan 6 02:17:29 PM EST 2023"}, { "textDescription", "This is a Description"} };
const boost::json::string& s = testobj.at("properties").at("textDescription").as_string();
cout << s << endl;
testobj.at("properties").at("textDescription").as_string() = "Hello, World!"; // ERROR
boost::json::string t = testobj.at("properties").at("textDescription").as_string();
cout << t << endl;
return 0;
}
At the noted ERROR line, the following error is generated.
error: passing ‘const boost::json::string’ as ‘this’ argument discards qualifiers [-fpermissive]
testobj.at("properties").at("textDescription").as_string() = "Hello, World!";
^~~~~~~~~~~~~~~
According to the documentation for value::as_string, there are two overloaded as_string() methods, one returning string& and the other returning string const&. Presumably, a returned string& would allow modification of the value, but I can't figure out how to access that particular overload of the method. My understanding was that if the instantiated class was const, then the const overload would be used, and otherwise the non-const overload would be used, but my instantiation is non-const and yet it's not working. Can anyone point out what I'm doing wrong here or provide some guidance on some other way to modify a value like this in a boost::json::object?
Note that I also tried getting a non-const boost::json::string& s reference and assigning a new string to it, but the compiler complained about that as well. Otherwise, everything else in the code above works as expected if the three lines preceding the return 0 line are commented out, so it's just the ERROR line that's problematic.
Non-const value::at overloads were added in version Boost 1.80.0: https://github.com/boostorg/json/commit/95a6297480ec403ccc2cf17ff02476dc7614f60c
It fixes their issue #703:
value::at() returns value with const qualifier even if the value is
non-const data.
Example
value jv1 = {{"key", 1}};
auto& a1 = jv1.as_object().at("key"); // returns value&
auto& a2 = jv1.at("key"); // returns const value&
value& a3 = jv1.at("key"); // compile error
value jv2 = {1, 2, 3};
auto& b1 = jv2.as_array().at(0); // returns value&
auto& b2 = jv2.at(0); // returns const value&
value& b3 = jv2.at(0); // compile error
Note: value.at_pointer() has no such issue. It returns const value&
if the data is const-qualified and returns value& otherwise.
Workaround for Boost 1.79.0:
This works in Boost 1.79.0:
if (auto p = testobj.if_contains("properties"))
if (auto td = p->as_object().if_contains("textDescription"))
td->as_string() = "Hello, World!";
Or even just
testobj["properties"].as_object()["textDescription"] = "Even better?";
See it Live On Boost 1.76.0

Check if function declared after Template first Argument is NULL (Passing Vector & Array)

While working with one of my other projects I came across what I believed to be an overloading error. I opened a new project, researched about overloading and here is quick code:
#include <iostream>
#include <vector>
#include <string>
template<class T, class A>
void template_Function(T first_Arg, A second_Arg)
{
if (first_Arg == NULL){
std::cout << "First argument of the template function is null." << std::endl;
std::cin.get();
return;
}
int main()
{
//Declare and assign values to vector.
std::vector<std::string> my_Vector;
my_Vector.push_back("Hello, Friend");
//Declare and assign values (using for loop) to array.
int my_Array[10];
for (int i = 0; i < 10; i++)
{
my_Array[i] = i;
}
//Attempting to pass BOTH the vector and array to the template function.
template_Function(my_Vector, my_Array);
std::cin.get();
return 0;
}
If I run this I get the error code C2678: binary '==' etc. I solved that issue by including these lines of code:
template<class T, class A>
void operator==(const T &q, const A &w);
Right after I included the headers. The new error states,
error C2451: conditional expression of type 'void' is illegal c:\users\axiom\documents\visual studio 2013\projects\_test_template\_test_template\source.cpp 11 1 _test_Template
I THINK it means, from all the googling that I can't compare "first_Arg" with NULL. Which is pretty much what I'm trying to do, see if the first_Arg is null and then go from there.
Thank you for any assistance.
You are passing a value-type (vector) to a function, but then try & compare it with a pointer (NULL). That can't work.
So either you declare your function to take a parameter T*, forcing you to pass my_Vector using &my_Vector, or you switch to reference semantics (const if you like), and don't compare with NULL at all.

Getting information out of a set<MyClass> mySet

So I've got
set<MyClass> mySet;
and in MyClass I have a static int to count the number of compares that happen. I'm trying to get that information out of it but I can't figure out how.
This is what I've tried:
set<MyClass>::iterator it = mySet.begin();
int count = it->getCompareCount();
and
int count = mySet.begin()->getCompareCount();
Neither of those work (Yes I know the are essentially the same exact thing) but I can get that information out of something that is indexed like a std::list or std::vector
Example:
vector<MyClass> myVector;
for (int i = 0; i < 10; i ++)
{
myVector.push_back(MyClass(i,"Some Name", i*2);
}
int count = myVector.at(2).getCompareCount(); //which by default is going to be 0 as no compares have taken place
Can anybody help me? netbeans says "error: passing ‘const MyClass’ as ‘this’ argument of ‘int MyClass::getCompareCount()’ discards qualifiers [-fpermissive]" when I do what I said I tried up above.
EDITS
Declaration of getCompareCount():
int MyClass::getCompareCount()
{
return compareCount;
}
Element in std::set is not mutable after it is inserted into the set. When you dereference an iterator (as in it->), it returns a const reference of the object you put into the set. On the other hand, your definition of getCompareCount() is a non-const instance function meaning it can only be called on non-const instance of the class. Define the function as the following should solve your problem.
int MyClass::getCompareCount() const
{
return compareCount;
}
By the way, since compareCount is a static int, you might want to define the function as static as well so that you can call it using the class instead of having to get an instance to call it.
static int MyClass::getCompareCount()
{
return compareCount;
}
//To Call it
MyClass::getCompareCount()

Can't return std::vector in a C++ function, compiling error

I'm having the following error while trying to compile the snippet below (using g++):
error: invalid initialization of non-const reference of type
‘std::vector<pos,std::allocator<pos> >&’ from a temporary of
type ‘std::vector<pos, std::allocator<pos> >&
(*)(std::vector<pos, std::allocator<pos> >&)’
This is the code that generates the error:
struct pos{
int start;
int end;
int distance;
int size;
};
bool compare_pos(pos a, pos b)
{
if (a.distance != b.distance)
return (a.distance < b.distance);
else
return (a.size < b.size);
}
vector<pos> sort_matches(vector<pos>& matches)
{
//vector<pos> sorted_matches(matches);
vector<pos> sorted_matches();
//sort(sorted_matches.begin(), sorted_matches.end(), compare_pos);
return sort_matches;
}
The real code would have the two commented lines uncommented, but even the commented example gives me the error. What am I doing wrong?
vector<pos> sorted_matches();
this declares a function that takes nothing and returns a vector<pos>. This is known as the most vexing parse. If you don't believe me, imagine the variable is named f instead of sorted_matches:
vector<pos> f();
Looks like a function, doesn't it?
Use this to define a default-constructed object:
vector<pos> sorted_matches;
return sorted_matches;

Assigning default values to arguments passed by reference

I want to do something like this:
int displayAll(Message *m, string &lastIndex, int &NumPrinted = 0 );
It gives me error, cribbing about int to int&.
I tried this too:
int temp =0;
int displayAll(Message *m, string &lastIndex, int &NumPrinted = temp );
Still it gives following error:
error: ISO C++ forbids in-class initialization of non-const static member 'temp'
Even static int temp; gives error.
error: ISO C++ forbids in-class initialization of non-const static member 'temp'
The problem with the first line of code you mention is that you are trying to pass a reference to a temporary variable
class Foo {
int displayAll(Message *m, bool &moreElements, string &lastIndex, int &NumPrinted = 0 );
};
The second bit of code complains because you were trying to initialize a class member statically.
class Foo {
int temp =0;
int displayAll(Message *m, bool &moreElements, string &lastIndex, int &NumPrinted = temp );
};
(I am putting your code inside of a class declaration to be clear about what is happening).
An easy way out of your problem that does not introduce a static variable is explicit function overloading:
class Foo {
inline int displayAll(Message *m, bool &moreElements, string &lastIndex) {
int dummy = 0;
return displayAll(m, moreElements, lastIndex, dummy);
}
int displayAll(Message *m, bool &moreElements, string &lastIndex, int &NumPrinted);
};
There's a bit of boilerplate, but it achieves what you want.
Hope this helps.
EDIT: Some more clarification. The core of the problem stems from the fact that the function must take a reference to some memory that it can modify. If you pass it a temporary variable (temporary as in the C++ meaning of the term, not just the english language term ) (as in your first line of code), it's illegal C++, since you usually copy a temporary to a value before you use it as an argument to a function:
void bar( int someNum = 0 ); // think of this as creating a temporary rvalue 0
// and then copying it into the function for use.
// temporary rvalues arise in expressions like
int v = 5 + 5; // the result of 5 + 5 is stored in a temporary rvalue, and then
// copied into v (which is an lvalue in this case).
So we need something that is an "lvalue", either some global variable somewhere or a temporary local variable ( in the english language sense ) as I gave in my answer. I was about to write a solution using a static variable, but there is a large flaw- since the static variable will be shared by all instances of your class, it will start out 0 and then be different every time you call the method ( since it would have been edted by the previous call). Even worse, in the case of multiple threads, you would be reading/writing to the same place of memory from several processors, so the value will be complete garbage, and you ill rape your processor cores' caches as each write will invalidate the cache of every other core. It's ugly, please don't do it. :P
By using my first solution you make the temporary variable very local, without much impact on anything else.
You can't do this for a non-const reference unless you declare temp to be static: see this stackoverflow post.
I fpund this interesting way of achieving this too:
class demo {
public:
void displayAll(int &x, int y = 0 ) {
int *p;
if(y)
p = (int*)y;
if(p) *p = 10;
x = 4;
}
};
int main() {
int x=0, y=0;
demo *obj = new demo();
obj->displayAll((x);
//obj->temp(x,(int)&y);
cout << "\n x= " << x << " y " << y;
return 0;
}