Newsgroup: it.comp.lang.c++
Titolo: set_new_handler in VC++

Ciao a tutti,
ho scritto  dei programmini usando la set_new_handler  come suggerito nello
Stroustroup e come da standard, se ho ben capito.
A  dire il  vero non  ho provato  ad allocare  troppa memoria,  per  cui la
funzione  che  gestisce la  mancanza  di  memoria  non vien  mai  chiamata,
tuttavia il programma compila e funziona sotto egcs.
Sotto VC++, invece, il programma compila ma l'eseguibile muore subito con:
  -->Assertion failed: new_p == 0, file handler.cpp, line 114
Ho  visto  che  posso  invece  usare  la  _set_new_handler,  che  ha  delle
convenzioni di  chiamata un po'  diverse, ma mi  chiedevo se in  effetti e`
possibile  usare  la  set_new_handler  in  VC++ e  come,  visto  che  anche
chiedendo ad  amici che dispongono del  Knowledge Base non  sono riuscito a
venirne a capo.
Grazie a chi legge, e ancora piu` a chi risponde :)

possibile  usare  la  set_new_handler  in  VC++ e  come,  visto  che  anche
VC++ e` accompagnato (in versioni ragionevolmente recenti)
da due versioni di librerie: una standard, una di compatibilita`
col passato.  NON vanno mescolate.  Quella standard, come da
standard, si ottiene con gli include SENZA .h:
    #include <new>
ad esempio, per quello che ti interessa; e, come da standard,
pone tutti i nomi nel namespace std, ecc ecc.  Usando questa
non dovresti avere problemi con set_new_handler (ovviamente
come std::set_new_handler o con opportuno using!).
Se invece vuoi usare la libreria di compatibilita` all'antica
(new.h, iostream.h, ecc), allora _set_new_handler.
NON mischiare le due, perche` i problemi sono quasi sicuri...!
Alex

 writes:
   VC++ e` accompagnato (in versioni ragionevolmente recenti)
   da due versioni di librerie: una standard, una di compatibilita`
   col passato.  NON vanno mescolate.  Quella standard, come da
   standard, si ottiene con gli include SENZA .h:
       #include <new>
   ad esempio, per quello che ti interessa; e, come da standard,
   pone tutti i nomi nel namespace std, ecc ecc.  Usando questa
   non dovresti avere problemi con set_new_handler (ovviamente
   come std::set_new_handler o con opportuno using!).
 
   Se invece vuoi usare la libreria di compatibilita` all'antica
   (new.h, iostream.h, ecc), allora _set_new_handler.
Grazie dell'informazione,  che ignoravo totalmente per via  della mia molto
settoriale  conoscenza del  C++ e  della quasi  nulla conoscenza  del VC++.
Tuttavia, seguire le tue istruzioni non sposta il mio problema :-(
Ho  infatti modificato  il sorgente  come segue,  compila  regolarmente, ma
ottengo sempre  lo stesso errore  all'esecuzione: assert fallita  (new_p ==
0).  Come se VC++ avesse  bisogno di una qualche inizializzazione su questo
new_p.  Ma  ricerche sulla  knowlwdge base non  mi hanno aiutato  in questo
senso.  Eppure la set_new_handler esiste,  quindi in qualche modo si dovra`
poter usare!
================================================================
#include <iostream>
#include <new>
using namespace std;
void 
memerr () 
{
  cerr << "memerr" << endl; 
  exit(1);
}
int
main ()
{
  set_new_handler(memerr);
  return 0;
}
================================================================

poter usare!
Oops -- no, i commenti in vc\crt\src\setnewh.cpp sono chiarissimi:
"""
WARNING : set_new_handler e` una funzione stub che e` fornita
_solo_ per permettere la compilazione dei template della libreria
standard (STL).
NON usatela per registrare un nuovo handler.  Usate invece
_set_new_handler.
Tuttavia, puo` essere chiamata per rimuovere l'handler attuale:
    set_new_handler(NULL);    // chiama _set_new_handler(NULL)
"""
Bisogna ammettere che e` difficile immaginare dei commenti piu`
espliciti di questi.  Peccato, pero`, che, gia` che c'erano, non
abbiano completato il supporto allo standard per questo piccolo
dettaglio!
La differenza e`, in sostanza: la set_new_handler dello Standard
accetta un puntatore a una funzione che *non prende nessun
argomento* e *non torna nulla*; essa viene chiamata se mai
l'operator new (standard; non uno ridefinito dall'utente) fallisce,
e puo` venire chiamata da altre "funzioni di allocazione"; essa
puo` fare tre sole cose (18.4.2.2):
    -- "rendere disponibile altra memoria", e tornare,
    -- chiamare abort o exit,
    -- lanciare un'eccezione bad_alloc (o derivata da essa).
In particolare, NON puo`, portabilmente, chiamare nessuna
funzione di sistema, ad esempio per fare dell'output (infatti
questo _potrebbe_ implicitamente coinvolgere allocazione di
memoria, causando "undefined behaviour").
La versione Microsoft, invece, accetta un argomento di tipo
size_t (che indica quanti byte erano richiesti nella chiamata
a operator new che e` fallita), e ritorna un valore intero (zero
per indicare il fallimento, non-zero per chiedere che l'operator
new riprovi a fare l'allocazione); puo` anche lanciare eccezioni,
terminare il programma, fare I/O, eccetera.
In altri termini, la versione non-Standard che _set_new_handler
mette a disposizione e` un "soprainsieme stretto" di quanto lo
Standard richiede, e quindi non sarebbe propro costato molto
a MS permettere di "ritagliare" le piu` limitate funzionalita` che
lo Standard specifica!
Fortunatamente, e` cosi` facile questo "ritaglio", che possiamo
agevolmente compierlo noi stessi, nel nostro codice utente!
_set_new_handler accetta e ritorna il tipo _PNH, puntatore a
funzione che prende un size_t e torna un int; set_new_handler
accetta e ritorna il tipo new_handler, puntatore a funzione
senza argomenti che torna void.  Possiamo dunque, con tutti
i dovuti #include, namespace, ed eventualmente #define
per toglierci dai piedi lo "stub" di set_new_handler della MS,
includere nel link il nostro file vero_snh.cpp...:
    static new_handler ph;
    static int _vero_handler(size_t) {
        if(ph) ph();
        return ph!=0;
    }
    new_handler set_new_handler(new_handler p)
    {
        new_handler vecchio = ph;
        ph = p;
        if(p) {
            _set_new_handler(_vero_handler);
        } else {
            _set_new_handler(0);            
        }
        return vecchio;
    }
Tutto qua!  Basta nascondere all'handler l'informazione
sulla dimensione richiesta, e ignorare la possibilita` di
segnalare il fallimento col valore di ritorno (senza, cioe`,
dover necessariamente lanciare una exception).  Perche`
MS non abbia definito cosi` il suo "stub", non si capisce...
Alex