C++ Logger

Ultimamente non stavo più andando avanti con progetti di programmazione, ma, riprendendo, ho sentito la necessità di fare un buon sistema di logging basato sugli stream di C++.

In passato avevo già affrontato l’argomento, ma questa è stata una sfida per vedere se riuscivo a fare tutto da capo e da solo. Ce l’ho fatta, così ho deciso di pubblicare qui i risultati.

Come requisiti ho tenuto in mente la versatilità e la sicurezza per l’uso con i thread.

La versatilità l’ho ottenuta mediante la divisione per modulo (per esempio networking, audio etc…) e livello (errore, informazione…). A coordinare tutto c’è il Log Manager, una classe che coordina i vari log. È un singleton (ottenuto tramite la mia classe allegata), e ritorna degli stream, uno per ogni coppia modulo-livello (che insieme fanno il LogId), assicurandosi che queste istanze siano uniche. Inoltre dispone di una funzione per mandare direttamente in output una stringa, dove in più si può specificare anche il file e la linea, magari usando le macro __FILE__ e __LINE__.

Ho pensato come vari output lo standard output e error, in più un file e un’eventuale consolle interna al programma, configurabili per ogni log, con una questa priorità: LogId > Modulo > Livello > Configurazione globale.

Come dipendenze ha gli smart pointer e i mutex/recursive mutex di Boost. Comunque se preferite e potete, C++11 ha integrato tutti questi componenti nella libreria standard, quindi potete usarli da là, ma sinceramente non ho provato a farlo.

Mancano, volendo, una funzione per impostare la configurazione “grezza” tramite gli enum e l’operatore OR e un controllo nei metodi overflow, xputn e sync della classe LogStreamBuf che ritornino subito se l’output è disabilitato, senza fare movimenti di memoria.

Ho fatto un paio di test anche con dei thread e non ho avuto problemi.

Non mi metto a scrivere tutte le funzioni perché mi pare siano già abbastanza spiegate nell’header 😉 .

Rilascio il materiale sotto il pubblico dominio.

Download: cpp_logger.tar.gz

PHP: non ci sono più abituato

In questi giorni nel poco tempo libero sto cercando di sistemare i problemi del comment center e di aggiungere qualche nuova funzionalità.

Nell’ultimo anno ho scritto sempre meno software in PHP e si è visto anche dagli aggiornamenti per i miei plugin di Flatpress. Mi sono invece dedicato molto al game programming in C++ più qualche lavoretto in C.

Ciò ha fatto molto bene alle mie facoltà da programmatore (o se non posso essere definito così almeno lasciatemi la libertà di chiamarmi almeno «scrittore di codice» 😌 ).

C e C++ sono linguaggi compilati e fino a poco più di un anno fa ero stato a contatto prettamente con linguaggi interpretati o linguaggi di scripting.

La cosa in cui avevo avuto più problemi era la gestione della memoria, infatti negli script è gestita automaticamente. C++ mi ha insegnato a pensare se veramente serve una variabile oppure no, mi ha insegnato la tipizzazione e l’uso della memoria dinamica. Adesso le apprezzo molto, nonostante i problemi iniziali.

Tuttavia non è stata questa la cosa più utile. Ciò che più si impara con un linguaggio come C++ è che non ci si può permettere di fare un codice disordinato. … [Leggi il resto]

Prendere la directory dell'eseguibile in C++

Talvolta può risultare utile sapere il percorso di un programma dal programma stesso. Un esempio pratico è quando bisogna caricare file come quelli di configurazione o risorse del programma.

Purtroppo non esiste un metodo cross-platform per farlo. Molti compilatori usano argv0, però non è obbligatorio che lo facciano. Poi ogni piattaforma ha il suo metodo.

Purtroppo neanche boost provvede questa funzionalità, sebbene abbia una libreria sul filesystem.

Basandomi su vari metodi trovati su internet (dentro al file cpp ci sono tutti i link), ho fatto una classe, il cui scopo è gestire il filesystem, che, basandosi sul percorso dell’eseguibile, risale alla root dell’applicazione. Per il momento questa è l’unica funzionalità, in seguito ne aggiungerò altre per miei scopi.

Finora l’ho provata solo su Linux, perciò se qualcuno riesce a provarlo su altre piattaforme me lo faccia sapere.

Sono richieste le librerie boot filesystem e boost system, in quanto le uso per fare i controlli e ottenere il percorso assoluto e senza collegamenti.

Sorgente e header li potete scaricare qui: filesystem_cpp.tar.gz.

Lo rilascio in pubblico dominio, non ci dovrebbero essere problemi per questi frammenti di codice.

Piccola guida: innanzitutto c’è da notare che uso il singleton pattern, ovvero un’unica istanza di cui si può prendere il puntatore tramite il metodo statico getSingletonPtr o la reference tramite getSigleton, comunque con il metodo fromArguments(int argc, char **argv) impostate gli argomenti della main, se vi interessa prendere l’eseguibile da lì; invece appRoot restituisce una reference const dell’oggetto boost::filesystem::path con la root dell’applicazione.

Attenzione: si presuppone che l’eseguibile sia in app-root/altra-directory/eseguibile. Se fosse direttamente in app-root dovete modificare LEVELS in 1.

Bullet e pendii

Se avete avuto a che fare con la libreria per la simulazione della fisica Bullet e dei pendii (colline, rampe), sarete sicuramente impazziti cercando di risolvere il problema per cui il corpo non sta fermo nel pendio.

È capitato anche a me, così dopo ore perse dietro, cambiando appena una parola chiave su google ho trovato la soluzione: come riportato qui, dovete fare così:

World->getDispatchInfo().m_allowedCcdPenetration = 0.0001f;

dove world è il vostro Bullet World.

Se usate Panda3D per ottenerlo dovete fare world->get_world().

Spero sia di aiuto a qualcuno!

Panda3D e Broadcast

Tempo fa avevo cercato di creare la parte di networking per un videogioco che utilizza il motore Panda3D.

I giochi hanno bisogno di aggiornare velocissimamente le posizioni degli altri giocatori e tutti gli altri dati, perciò si usa UDP, un protocollo che è molto più veloce del TCP perché non fa controlli per vedere se sono arrivati i dati (e perciò è anche meno affidabile, ma non importa per un videogioco).

Una delle caratteristiche più belle di UDP è anche quella di poter fare il “broadcasting” di rete, ovvero mandare a tutti i client di una rete un pacchetto in una certa porta. Questa caratteristica può essere usata per esempio per fare la lista dei server a cui connettersi.

Panda3D teoricamente offre una funzione chiamata set_broadcast nella classe NetAddress, però non funziona, né in Python, né in C++. O almeno, non da sola.

In Python alla fine avevo lasciato perdere, però in C++ invece volevo sistemarla. Non riuscivo proprio a capire come fare, così ho cercato e ho visto che in generale bisogna dire al sistema operativo che la socket deve poter eseguire il broadcast.

Il set_broadcast sembra poterlo fare, ma se in realtà guardiamo il codice è solo una scorciatoia per inserire come indirizzo IP 255.255.255.255 necessario per fare appunto il broadcast ma non sufficiente.

Spulciando un po’ il codice ho trovato che serve abilitare il broadcast alla socket. Ed ecco come fare.

Quando creiamo la socket dal manager, otteniamo un puntatore alla connessione. Un metodo di quest’istanza è il get_socket, che normalmente restituirebbe un’istanza di Socket_IP, tuttavia questa classe non ha il metodo che ci serve. Allora dobbiamo “trasformarla” in una Socket_UDP, tramite il casting. Allora in quella possiamo chiamare il SetToBroadCast() e per magia il tutto funzionerà.

Tradotto in codice 😉 :

	Socket_UDP * sock = (Socket_UDP *)connection->get_socket();
	sock->SetToBroadCast();

connection è la connessione creata tramite il QueuedConnectionManager.

Una volta aggiunto questo come per magia il tutto funzionerà 😊