IRC-Bot Klassendesign



  • Bitte einfach nur die Deklartionen, wo nötig Kommentare. Bitte kein UML. Noch viel viel besser wäre Prosa, aber das schaffen wenige so hinzukriegen, daß es einerseits nicht zusammenhanglos gestammelt ist, und andererseits nicht leer ist. Eine nette Mischung mach mal, beschreibe es so, daß ich es verstehe, obwohl ich es nicht vorher schon kannte.



  • Nun, ich habe mir gedacht, für die Events einen event_manager oder etwas vergleichbares zu basteln, der die Eventhandler intern abspeichert und am Socket ständig liest. Der soll dann die Nachrichten parsen und die entsprechenden Handler aufrufen.



  • Dann kannst du das auch mal tun, indem du einfach mal die Klassen dazu erstellst. Die nötigen öffentlichen Funktionen dazu und dann in etwa wie die zusammen arbeiten sollen. Dann siehst du relativ schnell ob das gut zusammenspielt und wo du Sachen verallgemeinern kannst, damit es erweiterbarer wird.

    Ich weiss nicht ob es so viel bringt das hier breit auszudiskutieren. Du kannst mal die grobe Struktur machen, denken, implementieren. Am Ende weisst du dann ja ob es gut war oder nicht. Du kannst natürlich im vornherein Ratschläge hier einholen, aber imo lernt man so Sachen besser, wenn man das einfach mal selber macht und daraus lernt. Und beim nächsten mal kannst du es besser machen.



  • @volkard: Prosa?
    Hier mal die Deklaration:

    #ifndef SOCKET_HPP
    #define SOCKET_HPP
    
    #include <string>
    #include <vector>
    
    #include <boost/asio.hpp>
    #include <boost/noncopyable.hpp>
    
    namespace irc
    {
    	// Um code übersichtlicher zu machen
    	namespace io
    	{
    		using namespace boost::asio;
    		typedef ip::tcp::socket socket;
    		typedef ip::tcp::resolver resolver;
    	}
    
    	// Stellt eine _eindeutige_ Verbindung zum IRC dar
    	class socket : public boost::noncopyable
    	{
    		const std::string server;
    		std::string nick; // Wichtig für Identifizierung, von einem Host, können mehrere Clients kommen
    		const unsigned short port;
    
    		io::streambuf buffer; // Lesepuffer
    		io::socket sock; // "roher" TCP Socket
    
    		std::vector<std::string> split(const std::string& str, const std::string& delim); // Hilfsfunktion um einen String anhand von Leerzeichen zu splitten
    
    		// \r\n wird abgeschnitten / angehängt
    		std::string read_line();
    		void write_line(const std::string& line);
    
    	public:
    		socket(io::io_service& io_service, const std::string& server, const std::string& nick, unsigned short port = 6667);
    	};
    }
    
    #endif // SOCKET_HPP
    


  • 314159265358979 schrieb:

    @volkard: Prosa?
    Hier mal die Deklaration:

    #ifndef SOCKET_HPP
    #define SOCKET_HPP
    
    #include <string>
    #include <vector>
    
    #include <boost/asio.hpp>
    #include <boost/noncopyable.hpp>
    
    namespace irc
    {
    	namespace io
    	{
    		using namespace boost::asio;
    		typedef ip::tcp::socket socket;
    		typedef ip::tcp::resolver resolver;
    	}
    
    	class socket : public boost::noncopyable
    	{
    		const std::string server;
    		std::string nick;
    		const unsigned short port;
    
    		io::streambuf buffer;
    		io::socket sock;
    
    		std::vector<std::string> split(const std::string& str, const std::string& delim);
    
    		std::string read_line();
    		void write_line(const std::string& line);
    
    	public:
    		socket(io::io_service& io_service, const std::string& server, const std::string& nick, unsigned short port = 6667);
    	};
    }
    
    #endif // SOCKET_HPP
    

    Jupp, jetzt reden wir eine Sprache.
    Prosa: Schreib normale deutsche Sätze dazu, was, warum, weshalb und warum nicht.

    Du schreibst, ein socket HAT einen nick. Mööp.



  • Editiert, war das so gemeint?



  • Das ist alles noch ziemlich low-level. Setz höher an. Was eine Klasse für Daten-Member und Hilfsfunktionen hat interessiert erstmal nicht.

    Interessant ist erstmal was du überhaupt für Klassen hast, und für was die zuständig sind. Und wer wie mit wem kommuniziert.



  • hustbaer schrieb:

    Das ist alles noch ziemlich low-level. Setz höher an. Was eine Klasse für Daten-Member und Hilfsfunktionen hat interessiert erstmal nicht.

    Naja, die meisten Daten-Member sind schon höchst interessant. Viel interessanter, als diese oder jene öffentliche Methode, die eh problemlos ist.
    Wie hier, wo ich den nick im socket entdeckt habe.
    (Deswegen mag ich auch im Code die Members vorne.)



  • Wo würdest du den Nick denn sonst hingeben?



  • volkard schrieb:

    hustbaer schrieb:

    Das ist alles noch ziemlich low-level. Setz höher an. Was eine Klasse für Daten-Member und Hilfsfunktionen hat interessiert erstmal nicht.

    Naja, die meisten Daten-Member sind schon höchst interessant. Viel interessanter, als diese oder jene öffentliche Methode, die eh problemlos ist.
    Wie hier, wo ich den nick im socket entdeckt habe.
    (Deswegen mag ich auch im Code die Members vorne.)

    Also ich sehe das anders.
    Was den Nick angeht, das siehst du im Prinzip genau so im Ctor.
    Und ich finde gar nicht dass der dort nicht hingehört. Nur heisst die Klasse falsch, die sollte "irc_session" o.ä. heissen.



  • 314159265358979 schrieb:

    Wo würdest du den Nick denn sonst hingeben?

    Vielleicht in die Sitzung.



  • Ich habe mir hier einmal das Interface des event_managers überlegt.

    #ifndef EVENT_MANAGER_HPP
    #define EVENT_MANAGER_HPP
    
    #include "socket.hpp"
    
    #include <string>
    #include <functional>
    
    #include <boost/noncopyable.hpp>
    
    namespace irc
    {
    	enum event_type
    	{
    		USER_JOIN,
    		USER_PART,
    		USER_KICK,
    		USER_BAN,
    		USER_QUIT,
    		...
    	};
    
    	// alle funktionen thread-safe
    	class event_manager : public boost::noncopyable
    	{
    	public:
    		typedef std::function<void(std::vector<std::string> args, event_manager& instance)> event_handler;
    
    		event_manager(irc::socket& socket);
    
    		void register_handler(event_type type, const std::string& chan, event_handler handler); // channel-spezifischer handler
    		void register_handler(event_type type, event_handler handler); // globaler handler
    
    		void unregister_handler(event_handler handler);
    
    		void write_line();
    		std::string read_line();
    
    		std::string nick() const;
    		std::string server() const;
    		unsigned short port() const;
    	};
    }
    
    #endif
    


  • void unregister_handler(event_handler handler);
    

    Da bin ich aber echt gespannt, wie die Implementierung aussieht 🙂

    Eventuell ist auch Boost.Signal was für dich...



  • Nexus schrieb:

    void unregister_handler(event_handler handler);
    

    Da bin ich aber echt gespannt, wie die Implementierung aussieht 🙂

    Eventuell ist auch Boost.Signal was für dich...

    Ja ich habe daran gedacht, das mit boost::signal zu implementieren, genauer gesagt mit boost::signal2 🙂

    Allerdings ist mir gerade ein anderes Konzept eingefallen, das mir auch recht gut gefällt. Und zwar zwei abstrakte Basisklassen "packet" und "event" von denen dann weitere packet- und event-Klassen für jeden Event-Typ abgeleitet werden. Dadurch kann man anstatt nur die Argumente am Handler zu bekommen direkt mit den geparsten Daten arbeiten und muss sich die Strings nicht selbst zusammenbauen.



  • Ich denke an sowas:

    #ifndef DINGENS_HPP
    #define DINGENS_HPP
    
    #include "socket.hpp"
    #include <string>
    #include <boost/noncopyable.hpp>
    
    namespace irc
    {
    	class dingens : public boost::noncopyable
    	{
    	public:
    		dingens(irc::socket& socket);
    
    		void register_handler(event_handler* handler);
    		void write_line();
    		std::string read_line();
    
    		std::string nick() const;
    		std::string server() const;
    		unsigned short port() const;
    	};
    }
    #endif
    
    class event_handler
    {
       virtual void receive_line(std::string line)=0;
       //oder
       virtual void join/leave/msg/whisper/kick/ban/motd/...
    


  • Angst vorm inner platform effect hab.



  • volkard schrieb:

    Angst vorm inner platform effect hab.

    ?



  • 314159265358979 schrieb:

    volkard schrieb:

    Angst vorm inner platform effect hab.

    ?

    Ich baue Methoden und nenne sie join und leave und so.
    Du baust Dir ein Framework, setzt event_type/lamda-Paare ab, und so Sachen.
    Ich kann sogar Vererbung.
    Du kannst events an Default-Eventhandler weiterreichen lassen.
    Ich will nicht sagen, daß Dein Entwurf schlecht wäre.
    Mußt nur irgendwo zwischen
    http://en.wikipedia.org/wiki/God_object
    und
    http://en.wikipedia.org/wiki/Inner-platform_effect
    landen und am besten nicht zu weit am Rand.



  • Sorry, ich verstehe nicht was du mir damit sagen möchtest. Wo versuche ich denn so eine "God-Klasse" zu machen?



  • 314159265358979 schrieb:

    Sorry, ich verstehe nicht was du mir damit sagen möchtest. Wo versuche ich denn so eine "God-Klasse" zu machen?

    Nicht Du. Mir könnte es passieren, wenn ich aus Angst vor dem inner platform effect zu wenig trickse.


Anmelden zum Antworten