Hogyan olvassunk be overflow nélkül?

 ( Dragor | 2009. szeptember 21., hétfő - 13:07 )

Üdv!
Lassan két órás keresgélés töprengés után, szeretném valakinek a segítségét kérni a következő problémámval...(gondolom igen egyszerű a megoldása)
Az inputstreamből szeretnék adatot beolvasni, majd int-té konvertálni, a gondom viszont az, hogy overflownál kivételt kéne dobnom amire eleddig nem jöttem rá, hogyan kell, mármint hogy dönthetem el, hogy volt-e.
Előre is köszi a segítséget...

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

pont akkora mennyiséget olvass be, mint amekorra egy int:)

Kicsit bővebben lehetne, mert kezdő vagyok...

egészen konkrétan mit is szeretnél? (beolvasni inputstream-ről ez eléggé kevéske infó). Billentyűzetről beolvasni egy számot, vagy socketről, filebol, stb.

Ha jól értem: input stream -> char buffer (pl: scanf), char buffer -> integer (pl: atoi). Az atoi nem végez hibakezelést. http://linux.die.net/man/3/atoi
Ha csak ennyi a problémád, ezernyi megoldás van.

Egyébként másold be a kódot, úgy könnyebb megérteni mit is szeretnél. :)

int ReadNat(std::string msg, std::string errormsg )
{
int n;
bool hiba;
do{
std::cout << msg;
std::string str;
std::cin >> str;
n = atoi(str.c_str());
hiba = (n==0 && str!="0") || n<0;
if(hiba) std::cout<< errormsg << std::endl;
}while(hiba);
return n;
}

Először is, ha 'n' nem lehet negatív, akkor legyen már unsigned int. Másodszor pedig leírtam az előbb a megoldást. Az atoi() nem kezel hibát, de például az strtol() már igen. Vagy dobj össze gyorsan egy konvertáló függvényt, ott azt kezelsz le amit csak akarsz.

köszi, segítségeket, probléma megoldva....

Bár azt írtad, hogy probléma megoldva, azért hadd szóljak még hozzá, mert a kérdés az egyik kedvenc vesszőparipám :)

Először is azt javasolnám, hogy ne keverd a C-s és C++-os módszert. A C++ nem C. Igen, persze, "elérhető" benne a C; azonban annyira más a stílusuk, hogy nagyon nem hasznos a kettőt vegyíteni. Én legalábbis éles határt vonnék itt a (userspace) libc függvények és a syscall-ok között. Nyilván syscall-ok helyett nem tudsz mit használni (amelyek a man 2. fejezetében vannak), a man 3. fejezete helyett viszont használj tisztán C++-t.

Ennyi üres locsogás után nézzünk konkrétumot, bővebben lásd pl. innen indulva:

#include <iostream>

using namespace std;

int
main()
{
  int i;

  // manual check
  cin.clear();
  cin >> i;
  if (!cin.good()) {
    cerr << "failure detected in manual check\n";
    return 1;
  }

  // with exceptions -- clear control state before setting up exception mask
  // so that no exception is thrown immediately
  cin.clear();
  cin.exceptions(istream::eofbit | istream::failbit | istream::badbit);
  try {
    cin >> i;
  }
  catch (ios_base::failure& e) {
    cerr << "failure detected via exception\n";
    return 1;
  }
}

Első hiba:

2147483648
failure detected in manual check

Második hiba:

2147483647
2147483648
failure detected via exception

C-ben a következő függvényhez hasonlót szeretek használni (lásd strtol()):

#define _XOPEN_SOURCE 600

#include <stdlib.h> /* strtol() */
#include <errno.h>  /* errno */
#include <limits.h> /* INT_MIN */
#include <stdio.h>  /* fprintf() */

static int
long_parse(long *dest, const char *src, long min, long max)
{
  char *endptr;
  
  errno = 0;
  *dest = strtol(src, &endptr, 10);
  return ('\0' != *src && '\0' == *endptr && 0 == errno
      && min <= *dest && *dest <= max) ? 0 : -1;
}

int
main(int argc, char **argv)
{
  while (0 != *++argv) {
    long tmp;

    if (-1 == long_parse(&tmp, *argv, INT_MIN, INT_MAX)) {
      (void)fprintf(stderr, "unable to parse \"%s\" as an int\n", *argv);
    }
    else {
      int i;

      i = tmp;
      (void)fprintf(stdout, "\"%s\" parsed as int %d\n", *argv, i);
    }
  }

  return EXIT_SUCCESS;
}

Futtatva:

$ ./proba2 \
  '' \
  ' ' \
  '1 ' \
  ' 1' \
  a \
  9223372036854775808 \
  2147483648 \
  2147483647 \
  -2147483648 \
  -2147483649
unable to parse "" as an int
unable to parse " " as an int
unable to parse "1 " as an int
" 1" parsed as int 1
unable to parse "a" as an int
unable to parse "9223372036854775808" as an int
unable to parse "2147483648" as an int
"2147483647" parsed as int 2147483647
"-2147483648" parsed as int -2147483648
unable to parse "-2147483649" as an int

Nézzük, mit melyik részfeltétel nem teljesüléséért utasított el (miért úgy néz ki a long_parse(), ahogy):

  • "a": a "subject sequence" (ld. fenti linket) üres, ezért nincs konverzió. Ekkor a visszaadott érték 0, az endptr a sztring elejére mutat, az errno-nak viszont nem kötelező EINVAL-t felvennie. Így ezt az esetet a '\0' == *endptr részfeltétel fogja meg.
  • " ": ugyanaz az eset, mint "a", lásd fent.
  • "": ugyanaz az eset, mint "a", lásd fent, azzal a csúnya kiegészítéssel, hogy a sztring eleje, ahova az endptr mutat, egyből a vége is, ezért ezt a '\0' != *src fogja meg.
  • "1 ": '\0' == *endptr
  • "9223372036854775808": 0 == errno
  • "2147483648": *dest <= max
  • "-2147483649": min <= *dest

Miért kell az errno = 0; az egész elé: ha az strtol() nem állítja be -- lásd például sikeres parse-olásnál -- valamilyen értékre az errno-t, akkor az értékadás nélkül az errno értéke a 0 == errno vizsgálatban meghatározatlan lehetne.

Annak a #define-nak van bármiféle jelentősége a C-ben? :)

Amúgy teljesen igazad van a C és a C++ elválasztásában, sajnos én is szeretek C++ kódba C-t illeszteni, ami nem szép, de néha egyszerűbb. :(

Van, bizonyos fuggvenyek egy icipicit maskepp mukodnek ennek a valtozonak a beallitasa eseten.
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Annak a #define-nak van bármiféle jelentősége a C-ben? :)

Igen.

Ez a long_parse azert is jo, mert a strtol ele be lehet lokdosni olyasmiket is, hogy peldaul a whitespace karaktereket porgesse ki a sztringbol, igy parsolhatova valnak olyan stringek is, amik alapbol nem (pl a " 1" es a "1 " is).
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.