WelcomeWelcome | FAQFAQ | DownloadsDownloads | WikiWiki

Author Topic: How to define a variable compatible with npos in C++  (Read 572 times)

Offline Rich

  • Administrator
  • Hero Member
  • *****
  • Posts: 12124
How to define a variable compatible with npos in C++
« 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:
Code: [Select]
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:
Code: [Select]
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.

Offline Paul_123

  • Administrator
  • Hero Member
  • *****
  • Posts: 1405
Re: How to define a variable compatible with npos in C++
« Reply #1 on: May 10, 2025, 09:20:23 AM »
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 ) {

Offline Paul_123

  • Administrator
  • Hero Member
  • *****
  • Posts: 1405
Re: How to define a variable compatible with npos in C++
« Reply #2 on: May 10, 2025, 10:28:29 AM »
looked at the source.

in apps.cxx it uses the cast the other direction
Code: [Select]
  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.

Offline Rich

  • Administrator
  • Hero Member
  • *****
  • Posts: 12124
Re: How to define a variable compatible with npos in C++
« Reply #3 on: May 10, 2025, 05:59:56 PM »
Hi Paul_123
looked at the source.

in apps.cxx it uses the cast the other direction
Code: [Select]
  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:
Code: [Select]
  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.

Offline Paul_123

  • Administrator
  • Hero Member
  • *****
  • Posts: 1405
Re: How to define a variable compatible with npos in C++
« Reply #4 on: May 10, 2025, 07:57:46 PM »
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?
Code: [Select]

#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
Code: [Select]
$ ./cast
18446744073709551615
-1
18446744073709551615
-1
18446744073709551615
-1
18446744073709551615
$


So what is the problem you are trying to solve?

Offline Rich

  • Administrator
  • Hero Member
  • *****
  • Posts: 12124
Re: How to define a variable compatible with npos in C++
« Reply #5 on: May 11, 2025, 12:48:55 AM »
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:
Code: [Select]
std::string::size_type sloc = cmdline.find(target_boot_option);

Offline gadget42

  • Hero Member
  • *****
  • Posts: 877
Re: How to define a variable compatible with npos in C++
« Reply #6 on: May 11, 2025, 04:36:16 AM »
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_
The fluctuation theorem has long been known for a sudden switch of the Hamiltonian of a classical system Z54 . For a quantum system with a Hamiltonian changing from... https://forum.tinycorelinux.net/index.php/topic,25972.msg166580.html#msg166580

Offline Paul_123

  • Administrator
  • Hero Member
  • *****
  • Posts: 1405
Re: How to define a variable compatible with npos in C++
« Reply #7 on: May 11, 2025, 07:18:37 AM »
I just wanted to know the return type  for .find().
I think this looks right:
Code: [Select]
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


Offline Rich

  • Administrator
  • Hero Member
  • *****
  • Posts: 12124
Re: How to define a variable compatible with npos in C++
« Reply #8 on: May 11, 2025, 12:43:33 PM »
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:
Code: [Select]
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:
Code: [Select]
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=":
Code: [Select]
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:
Code: [Select]
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?

Offline Paul_123

  • Administrator
  • Hero Member
  • *****
  • Posts: 1405
Re: How to define a variable compatible with npos in C++
« Reply #9 on: May 11, 2025, 04:33:38 PM »
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.



Offline Rich

  • Administrator
  • Hero Member
  • *****
  • Posts: 12124
Re: How to define a variable compatible with npos in C++
« Reply #10 on: May 11, 2025, 09:25:26 PM »
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.

Offline Paul_123

  • Administrator
  • Hero Member
  • *****
  • Posts: 1405
Re: How to define a variable compatible with npos in C++
« Reply #11 on: May 12, 2025, 03:29:58 PM »
Was playing with Copilot in github today.

Prompt:  write code in c++ to parse the linux kernel command line

Code: [Select]
#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.