Designfrage
-
Ich arbeite an einem grösseren Algorithmus in C/C++ und bin es Leid dauernd zwischen den Dokumenten zu blättern.
Ich könnte mir viel Arbeit ersparen, wenn ich konsequent Klassen anlegen würde. Allerdings gäbe es von den meisten Klassen nur jeweils eine globale Instanz.
Folglich wären die Membervariablen und Funktionen alle static. Ist das aus der Sicht des Designs ok? Oder sollte so ein File mit den Daten klassenlos implementiert bleiben?
Hier ein Beispiel, wie es aussehen würde mit einer Klasse:
class Square { public: static const sintx FILE_A=0; static const sintx FILE_B=1; static const sintx FILE_C=2; static const sintx FILE_D=3; static const sintx FILE_E=4; static const sintx FILE_F=5; static const sintx FILE_G=6; static const sintx FILE_H=7; public: static const sintx RANK_1=0; static const sintx RANK_2=1; static const sintx RANK_3=2; static const sintx RANK_4=3; static const sintx RANK_5=4; static const sintx RANK_6=5; static const sintx RANK_7=6; static const sintx RANK_8=7; public: static const sintx COLOR_WHITE=0; static const sintx COLOR_BLACK=1; public: static const sintx SQUARES=64; static const sintx FILES=8; static const sintx RANKS=8; public: static const sintx square_file[SQUARES]; static const sintx square_rank[SQUARES]; static const sintx square_color[SQUARES]; private: static sintx square_file_distance[SQUARES][SQUARES]; static sintx square_rank_distance[SQUARES][SQUARES]; static sintx square_distance[SQUARES][SQUARES]; static sintx square_direction[SQUARES][SQUARES]; public: static void initialize(void); static char char_from_file(const sintx file); static char char_from_rank(const sintx rank); public: static sintx square_file_mirror(const sintx square); static sintx square_rank_mirror(const sintx square); static sintx square_from_file_rank(const sintx file, const sintx rank); static sintx square_get_file_distance(const sintx from, const sintx to); static sintx square_get_rank_distance(const sintx from, const sintx to); static sintx square_get_distance(const sintx from, const sintx to); static sintx square_get_direction(const sintx from, const sintx to); };
PS: sintx ist ein compilierungsabhängiger signed integer (32-Bit oder 64-Bit).
-
Bevor ich so etwas schreiben würde, würde ich Singletons benutzen. (über Sinn usw. sei von meiner Seite nichts diskutiert).
-
Die Konstanten sind alle public, da diese nicht manipuliert werden können. Nur die Tabellen, die nach der Initialisierung zur Verfügung stehen müssen über inline Memberfunctions abgefragt werden.
Von solchen Files habe ich unzählige in meinem Programm. Meist mit hunderten von Memberkonstanten und Look-Up-Tables. In statische Klassen verpackt, wäre es etwas einfacher den Überblick zu behalten und mit den Daten zu arbeiten.
-
Hallo zusammmen,
kurze Frage bzw Anmerkung: Inwiefern sind deine Methoden denn wirklich public, da static ja automatisch einen privaten Charakter mit sich bringt, da dem Linker ja nicht die Methode bekannt gegeben wird?!lg, freakC++
-
Diese Methoden sind kleine Inline-Functions, deren Zweck nur darin besteht, die initialisierten Tabellen auszulesen und einen Wert zurückzugeben:
inline sintx Square::get_file_distance(const sintx from, const sintx to) { return file_distance[from][to]; }Der Algorithmus ist zeitkritisch und daher vermeide ich natürlich überflüssige this-Zeiger (wäre ja auch sinnlos, wenn es keine Daten gibt).
-
Derzeit sieht es etwa so aus wie weiter unten. Nur dass es noch viel mehr Daten in mehreren Files sind und ich die Namen immer wieder vergesse.

In C++ mit Intellisense wäre es sicher einfacher. Allerdings will ich nicht gegen die Regeln des Programmierens verstossen. Bin ein Neuling und frage daher so blöde Sachen.

//// //// public bitboard constant vectors //// extern const uint64 bitboard_file[SQUARES]; extern const uint64 bitboard_rank[SQUARES]; extern const uint64 bitboard_bishop_magic[SQUARES]; extern const uint64 bitboard_rook_magic[SQUARES]; extern const sintx bitboard_bishop_shift[SQUARES]; extern const sintx bitboard_rook_shift[SQUARES]; //// //// public bitboard-bit vectors //// extern uint64 bitboard_set[SQUARES]; extern uint64 bitboard_clear[SQUARES]; extern uint64 bitboard_direction_minus9[SQUARES]; extern uint64 bitboard_direction_minus8[SQUARES]; extern uint64 bitboard_direction_minus7[SQUARES]; extern uint64 bitboard_direction_minus1[SQUARES]; extern uint64 bitboard_direction_plus1[SQUARES]; extern uint64 bitboard_direction_plus7[SQUARES]; extern uint64 bitboard_direction_plus8[SQUARES]; extern uint64 bitboard_direction_plus9[SQUARES]; extern uint64 bitboard_direction_square_minus8[SQUARES]; extern uint64 bitboard_direction_square_plus8[SQUARES]; extern uint64 bitboard_between[SQUARES][SQUARES]; extern uint64 bitboard_diagonal[SQUARES]; extern uint64 bitboard_antidiagonal[SQUARES]; //// //// public bitboard-pawn vectors //// extern uint64 bitboard_white_pawn_attack[SQUARES]; extern uint64 bitboard_black_pawn_attack[SQUARES]; extern uint64 bitboard_white_pawn_backward[SQUARES]; extern uint64 bitboard_black_pawn_backward[SQUARES]; extern uint64 bitboard_white_pawn_passed[SQUARES]; extern uint64 bitboard_black_pawn_passed[SQUARES]; extern uint64 bitboard_adjacent_files[SQUARES]; //// //// public bitboard-knight vectors //// extern uint64 bitboard_knight_attack[SQUARES]; //// //// public bitboard-bishop vectors //// extern uint64 bitboard_pseudo_bishop_attack[SQUARES]; extern uint64 bitboard_bishop[SQUARES]; extern uint64 bitboard_bishop_attack[0x1480]; extern sintx bitboard_bishop_index[SQUARES]; //// //// public bitboard-rook vectors //// extern uint64 bitboard_pseudo_rook_attack[SQUARES]; extern uint64 bitboard_rook[SQUARES]; extern uint64 bitboard_rook_attack[0x19000]; extern sintx bitboard_rook_index[SQUARES]; //// //// public bitboard-king vectors //// extern uint64 bitboard_king_attack[SQUARES]; //// //// public evaluation bitboards //// extern uint64 bitboard_white_in_front[SQUARES]; extern uint64 bitboard_black_in_front[SQUARES]; extern uint64 bitboard_square_color[2];
-
Tomahawk schrieb:
Von solchen Files habe ich unzählige in meinem Programm. Meist mit hunderten von Memberkonstanten und Look-Up-Tables. In statische Klassen verpackt, wäre es etwas einfacher den Überblick zu behalten und mit den Daten zu arbeiten.
Was hältst du von einem Namensraum? Gleicher Zugriff über
operator::wie bei Klassen, aber die Suche nach einer fragwürdigen Rechtfertigung von Klassen entfällt.
Du sprichst es ja schon an: Du brauchst keine
this-Zeiger. Es gibt eher wenige Situationen, in denen es sinnvoll ist, Klassen ohne Instanzen zu erstellen (z.B. abstrakte Basisklassen, von denen aber indirekt eigentlich auch Objekte konstruiert werden). Niemals denken, Klassen seien alleine deswegen objektorientierter oder sogar besser, nur weil es Klassen sind. Gleiches gilt natürlich für Memberfunktionen im Gegensatz zu freien Funktionen. Eine Klasse als Verpackung globaler Variablen (Singleton als Spezialfall) muss schon einen guten Grund mitbringen, um geeigneter als das naheliegendere Äquivalent zu sein.namespace Square { extern const sintx FILE_A = 0; } DoSomething(Square::FILE_A);Denk übrigens dran, bei
const-qualifizierten globalen Variablenexternhinzuschreiben. Ohne Angabe der Speicherklasse besitzen diese nämlich standardmässig interne Bindung (wiestaticauf globaler Ebene), wodurch in jeder Übersetzungseinheit eine eigene Definition auftritt. Sprich, du hast die gleiche Variable mehrmals.
-
Danke erstmal!
Deine Argumentation ist für mich überzeugend und ausreichend, um von der "gekünstelten" Objektorientierung wieder etwas Abstand zu nehmen.
Vererbung kommt bei mir so gut wie nie in Spiel. Daher mache ich es mir zur Regel: Gibt es keine Instanzen einer Klasse - meide ich auch den Klassenbegriff und arbeite weiterhin prozedural.
Namespaces sind allerdings eine nette Alternative, an die ich jetzt nicht gedacht habe. Einzelne Konstanten in Headers haben bei mir nie externe Bindung. Ist vielleicht etwas redundant für den Linker (stimmt doch, oder!?) aber ansonsten nicht wirklich aufblähend.
-
Danke erstmal!
Deine Argumentation ist für mich überzeugend und ausreichend, um von der "gekünstelten" Objektorientierung wieder etwas Abstand zu nehmen.
Vererbung kommt bei mir so gut wie nie in Spiel. Daher mache ich es mir zur Regel: Gibt es keine Instanzen einer Klasse - meide ich auch den Klassenbegriff und arbeite weiterhin prozedural.
Namespaces sind allerdings eine nette Alternative, an die ich jetzt nicht gedacht habe. Einzelne Konstanten in Headers haben bei mir nie externe Bindung. Ist vielleicht etwas redundant für den Linker (stimmt doch, oder!?) aber ansonsten nicht wirklich aufblähend.