Tiny Core Linux
General TC => Programming & Scripting - Unofficial => Topic started by: Rich on May 10, 2025, 08:59:39 AM
-
Several programs in Xprogs.tcz use a .find() instruction which returns
a value which is compatible with npos.
The value returned is unsigned and its width depends on the target it's
compiled for. Here is an example:
string cmdline, target_boot_option;
ifstream proc_cmdline("/proc/cmdline");
getline(proc_cmdline, cmdline);
proc_cmdline.close();
target_boot_option = "lst=";
int sloc = cmdline.find(target_boot_option);
if ( sloc == string::npos ) {
onbootName = "onboot.lst";
} else {
int eloc = cmdline.find(" ",sloc);
int work = eloc - (sloc + target_boot_option.length());
onbootName = cmdline.substr(sloc+target_boot_option.length(),work);
}
When compiled, the following warning is issued:
warning: comparison of integer expressions of different signedness: 'int' and 'std::__cxx11::basic_string<char>::size_type' {aka 'unsigned int'} [-Wsign-compare]
if ( sloc = string::npos )
From what I've read:
npos is size_type which is typedef as size_t
exception, if someone writes a custom class and defines it differently
What is the correct C++ way to:
define sloc so it is compatible with npos?
define eloc and work so the math doesn't blow up under other architectures?
I don't really understand C++, so I'm looking for someone who does.
-
That is just a compiler warning, C++ should implicitly do that conversion. But to make sure it’s doing what you want, explicitly do the typecasting.
if ( sloc == static_cast<int>string::npos ) {
-
looked at the source.
in apps.cxx it uses the cast the other direction
if ( (long unsigned)sloc == string::npos ) {
This is a C style typecast. One thing to note is that on aarch64, I do not see the same warning you do.
-
Hi Paul_123
looked at the source.
in apps.cxx it uses the cast the other direction
if ( (long unsigned)sloc == string::npos ) {
...
Yes, I did that, though I'm not sure if that's a smart way to handle it.
The problem is npos is a constant set to -1 and defined as unsigned.
It is used to indicate the find command failed by returning the largest
address the architecture supports.
sloc can't be an int because it's signed.
It can't be an unsigned int because on x86_64 that's 32 bits.
I'm just concerned that unsigned long causes an unforeseen
problem elsewhere.
I did try these 4 and the compiler didn't complain:
std::__cxx11::basic_string<char>::size_type loc = cmdline.find("norestore");
std::basic_string<char>::size_type loc = cmdline.find("norestore");
size_t loc = cmdline.find("norestore");
string::size_type loc = cmdline.find("norestore");
But I don't know enough about C++ to know if any of
them are appropriate.
-
I thought you were asking about the compiler warning. not asking was it working right.
string::npos is a very large number. The standard casts it the other way to define it. Take a -1 and cast it unsigned.
I think its working fine. Does this help explain?
#include <iostream>
#define NEG1 -1
int main(){
//outputs npos.....very big number
std::cout << std::string::npos << std::endl;
//This gives a compiler warning for the unsigned to signed conversion
int sloc = std::string::npos; //sloc will = -1
std::cout << sloc << std::endl;
// sloc is a -1 casting to a unsigned long: yields the very large number
std::cout << (unsigned long)sloc << std::endl;
//just a few other examples.
//This casts everything as an signed int.
int eloc = (int)std::string::npos;
std::cout << eloc << std::endl;
//This casts everything as a unsigned long
unsigned long ul = std::string::npos;
std::cout << ul << std::endl;
//This is just casting a defined constant.
std::cout << NEG1 << std::endl;
std::cout << (size_t)NEG1 << std::endl;
return 0;
}
The resulting output
$ ./cast
18446744073709551615
-1
18446744073709551615
-1
18446744073709551615
-1
18446744073709551615
$
So what is the problem you are trying to solve?
-
Hi Paul_123
... So what is the problem you are trying to solve?
I just wanted to know the return type for .find().
I think this looks right:
std::string::size_type sloc = cmdline.find(target_boot_option);
-
mostly for future thread visitors:
https://stackoverflow.com/questions/918567/size-t-vs-containersize-type
https://en.cppreference.com/w/cpp/types/size_t
however, it should be duly noted that anything/everything _on_and_or_traveling_through_the_tentacles_of_the_www_cyber_realm can/may/might/will be altered and/or possibly hallucinated via AI/LLM/LVM/ML/etc (so basically everything on the internet should be considered suspect at the very least)
more (who watches the watchers that watch the "good" ai that watches out for the "bad" ai?)
even more (so we need even more climate-destroying energy-hungry data-centers for all those "good" ai to find the "bad" ai... and then do what? to what? where/when/how? and why are the people pointing this out not being taken more seriously?)
rhetorical? off-topic? maybe... maybe_not... quantum_ai... singularity... https://hmpg.net/ https://endoftheinternet.com/ please_turn_the_lights_off_on_your_way_out_ok_thanks_bye_
-
I just wanted to know the return type for .find().
I think this looks right:
std::string::size_type sloc = cmdline.find(target_boot_option);
That’s is what it’s defined as, but there is nothing really wrong casting to an int either
-
Hi Paul_123
Thanks, I think I'll make all affected variables match the return type.
By the way, I think i may have found a potential bug in apps and appsaudit:
string cmdline, target_boot_option;
ifstream proc_cmdline("/proc/cmdline");
getline(proc_cmdline, cmdline);
proc_cmdline.close();
target_boot_option = "lst=";
int sloc = cmdline.find(target_boot_option);
if ( sloc == string::npos ) {
onbootName = "onboot.lst";
} else {
int eloc = cmdline.find(" ",sloc);
int work = eloc - (sloc + target_boot_option.length());
onbootName = cmdline.substr(sloc+target_boot_option.length(),work);
}
If I understand this correctly:
If "lst=" was found, we search for the next blank space separating boot options:
int eloc = cmdline.find(" ",sloc);
If someone placed the "lst=" option last, there is no blank space, and find returns npos.
Here we calculate the length of the file name after "lst=":
int work = eloc - (sloc + target_boot_option.length());
If the previous command returned npos , then eloc would be huge and so would work.
This copies that file name to onbootName:
onbootName = cmdline.substr(sloc+target_boot_option.length(),work);
If work contains a huge value, we now fall off the end of the array and seg fault.
Did I get that right, or did I miss something?
Does C++ do something behind the scenes I'm not aware of?
-
c++ string functions handles the end of the string more gracefully than c.
In your example work is in fact large but would produce the same result as using npos. The substr function accepts npos as the end position, and just returns from the start position to the end of the string.
-
Hi Paul_123
I did decide to test this for myself by adding lst=onboot.lst to the end of cmdline.
Result:
eloc was set to 4294967295
work was set to 4294967226
onbootName was correctly set to onboot.lst and no seg fault occurred.
Since a trailing space or end of string marker (\0) are both
acceptable in this case, it works correctly because C++
hid the error (or failed gracefully).
Thank you for the explanation.
-
Was playing with Copilot in github today.
Prompt: write code in c++ to parse the linux kernel command line
#include <iostream>
#include <fstream>
#include <sstream>
#include <map>
#include <string>
int main() {
const std::string cmdlinePath = "/proc/cmdline";
std::ifstream cmdlineFile(cmdlinePath);
if (!cmdlineFile.is_open()) {
std::cerr << "Error: Could not open " << cmdlinePath << std::endl;
return 1;
}
std::string cmdline;
std::getline(cmdlineFile, cmdline);
cmdlineFile.close();
std::map<std::string, std::string> cmdlineParams;
std::istringstream stream(cmdline);
std::string token;
while (std::getline(stream, token, ' ')) {
size_t equalPos = token.find('=');
if (equalPos != std::string::npos) {
std::string key = token.substr(0, equalPos);
std::string value = token.substr(equalPos + 1);
cmdlineParams[key] = value;
} else {
cmdlineParams[token] = ""; // Parameter without a value
}
}
// Display the parsed parameters
std::cout << "Parsed Kernel Command Line Parameters:" << std::endl;
for (const auto& param : cmdlineParams) {
std::cout << param.first << " = " << (param.second.empty() ? "(no value)" : param.second) << std::endl;
}
return 0;
}
Not drastically different than what we are doing. Copilot always used size_t for the result coming from find.