designfrage zu schnittstellenklasse und implementierung
-
gute morgen leute
ausgangspunkt ist folgende klasse:
class auth_params { public: auto account(void) const noexcept -> const std::string&; auto username(void) const noexcept -> const std::string&; auto password(void) const noexcept -> const std::string&; auto session_token(void) const noexcept -> const std::string&; auto api_key(void) const noexcept -> const std::string&; auto account(const std::string &str) -> auth_params&; auto username(const std::string &str) -> auth_params&; auto password(const std::string &str) -> auth_params&; auto session_token(const std::string &str) -> auth_params&; auto api_key(const std::string &str) -> auth_params&; private: std::string m_account; std::string m_username; std::string m_password; std::string m_session_token; std::string m_api_key; }; /* class auth_params */nun moechte ich die daten an eine funktion uebergeben, die in einer DLL residiert. dll-boundaries und std::string ist so ne sache, also brauch ich eine reine schnittstellenklasse die folgend aussieht:
class auth_params_interface { public: virtual auto account(void) const noexcept -> const char* = 0; virtual auto username(void) const noexcept -> const char* = 0; virtual auto password(void) const noexcept -> const char* = 0; virtual auto session_token(void) const noexcept -> const char* = 0; virtual auto api_key(void) const noexcept -> const char* = 0; }; /* class auth_params_interface */ein
class auth_params : private class auth_params_interface { public: auto account(void) const noexcept -> const std::string&; auto username(void) const noexcept -> const std::string&; auto password(void) const noexcept -> const std::string&; ... auto get_interface(void) const noexcept -> const auth_params_interface* { return *this; } private: virtual auto account(void) const noexcept -> const char*; virtual auto username(void) const noexcept -> const char*; ... };kommt leider nicht in frage, weil sich die gleichnamigen funktionen nur im return type unterscheiden.
also hab ich mir einen adapter gebaut:
class auth_params_interface_adapter : public auth_params_interface { public: auth_params_interface_adapter(const auth_params &p) noexcept; virtual auto account(void) const noexcept -> const char*; virtual auto username(void) const noexcept -> const char*; virtual auto password(void) const noexcept -> const char*; virtual auto session_token(void) const noexcept -> const char*; virtual auto api_key(void) const noexcept -> const char*; private: const auth_params &m_auth_params; }; /* class auth_params_interface_adapter */nun stellt sich mir gerade die frage ob ich den adapter in die auth_params klasse in den privaten bereich setze, weil ich dann via
auth_params::get_interfacemir die schnittstelle leicht holen kann. ansonsten muesste ich immer zuerst ein adapter objekt erstellen und dann mir die schnettstelle daraus holen.was meint ihr ? hat die adapterklasse da was in der auth_params was zu suchen oder soll ich die extern lassen ?
Meep Meep
-
Meep Meep schrieb:
dll-boundaries und std::string ist so ne sache
Ist das Problem eine unterschiedliche Implementierung der string-Klasse oder nur unterschiedliche Allokatoren?
-
camper schrieb:
Meep Meep schrieb:
dll-boundaries und std::string ist so ne sache
Ist das Problem eine unterschiedliche Implementierung der string-Klasse oder nur unterschiedliche Allokatoren?
in dem moment nichts von beiden. aber die DLL wird so schnell nicht mehr neu kompiliert werden sondern nur die programme die darauf zugreifen. also wird relativ bald ein anderer compiler verwendet werden. vielleicht auch von einem anderen hersteller. derzeit VS2015. Meep Meep
-
Also wenn ich es richtig verstehe, geht es darum, strings lesbar weiterzugeben, ohne dass std::string selbst im Interface vorkommt.
Was wäre mit einer einfachen Proxy-Klasse etwa:class string_view { string_view(const std::string& str) noexcept : str_( str.c_str() ), size_( str.size() ) {} std::string operator() const noexcept { return { begin(), end() }; } using iterator = const char*; using const_iterator = iterator; iterator begin() const noexcept { return str_; } iterator end() const noexcept { return str_ + size_; } std::size_t size() const noexcept { return size_; } std::size_t length() const noexcept { return size_; } const char& operator[](std::size_t i) const noexcept { return str_[i]; } private: const char* str_; std::size_t size_; };Jede Interface-Funktion der DLL, die eigentlich einen std::string-Parameter nähme, bekommt statt dessen einen string_view-Parameter. Innerhalb der Funktion kann dann ggf. wieder direkt ein std::string erzeugt werden, falls das notwendig sein sollte.
-
ich sehe jetzt vielleicht dir tragweite nicht, deshalb die frage: welchen vorteil hat der proxy gegenueber meinem adapter ?
Meep Meep
-
Meep Meep schrieb:
ich sehe jetzt vielleicht dir tragweite nicht, deshalb die frage: welchen vorteil hat der proxy gegenueber meinem adapter ?
Meep Meep
Dass keinerlei Änderungen auf Client-Seite und nur minimale Änderungen in der DLL erforderlich sind? Um ehrlich zu sein, habe ich noch gar nicht verstanden wie dein Adapter eingesetzt werden soll; klar ist aber, dass das keine transparente Angelegenheit ist.
-
der einsatz ist folgend:
das ist das interface
class auth_params_interface { public: virtual auto account(void) const noexcept -> const char* = 0; virtual auto username(void) const noexcept -> const char* = 0; virtual auto password(void) const noexcept -> const char* = 0; virtual auto session_token(void) const noexcept -> const char* = 0; virtual auto api_key(void) const noexcept -> const char* = 0; }; /* class auth_params_interface */in der DLL hab ich z.b. eine funktion der ich die parameter uebergebe:
auto set_auth_params(auth_params_interface *params) -> void;und so uebergeb ichs dann:
auth_params params; ... auth_params_interface_adapter adapter(params); set_auth_params(&adapter);das war jetzt ein vereinfachtes beispiel.
Meep Meep
-
Wieso nicht einfach nen struct aus Zeigern übergeben?
-
Meep Meep schrieb:
ich sehe jetzt vielleicht dir tragweite nicht, deshalb die frage: welchen vorteil hat der proxy gegenueber meinem adapter ?
Meep Meep
Ich dachte erst, der Vorteil an campers Klasse wäre, dass es ein POD-Typ ist und somit auch über verschiedene Compiler hinweg funktioniert.
Aber ich habe nochmal nachgeguckt und es heißt, ein POD darf keinen Konstruktor haben.Dürfte man die Klasse trotzdem durch verschiedene Compiler reichen?
Abgesehen davon finde ich jede Art von Proxy/Adapter/EigeneStringKlasse hier falsch.
C++-Binärkompatibilität macht einfach keinen Spaß.
Einfach C-Funktionen nehmen.
-
hustbaer schrieb:
Wieso nicht einfach nen struct aus Zeigern übergeben?
weil, in dem moment wo die DLL die zugangsdaten braucht, sie erst generiert werden, bzw aus einer quelle ausgelesen werden.
ich koennte genauso einen pointer auf eine funktion mituebergeben, damit die dll die daten zuerst anfordern kann und dann ueber die char-pointer darauf zugreifen kann. ich persönlicher finde es aber so eleganter.
zudem schreibt/liest die DLL die daten ueber rein-virtuelle klassen die aehnlich wie die ISerialStream klasse aufgebaut sind. da moechte ich nicht mischen anfangen.