Forum: PC-Programmierung c++ Klassenkonstanten


von Timm R. (Firma: privatfrickler.de) (treinisch)


Lesenswert?

Hallo,

ich habe immer wieder den Fall, dass ich an member Funktionen eine 
Klassenkonstante übergeben möchte um das erwünschte Verhalten zu 
signalisieren.

Ich mache das so:
1
public:
2
    using                   DeviceDirection = int;
3
    const DeviceDirection   InputDevice = 0;
4
    const DeviceDirection   OutputDevice = 1;

das ist eigentlich ganz nett und da nur ich selbst das api benutze, 
stört der Nachteil eigentlich auch nicht: Was mich stört ist, dass man 
einer Memberfunktion, zum Beispiel:
1
void MidiManager::enableDevice(int index, DeviceDirection);

als Konstante (DeviceDirection) jetzt auch einfach einen beliebigen int 
übergeben kann.

Das ist meine Frage. Geht es, und wenn ja, wie, dass ich die Sache so 
gestalten kann, dass ich nur genau die vorgesehenen Konstanten übergeben 
kann und nicht irgendwas konvertierbares?

Mit "nicht kann" meine ich so beiläufig oder aus Versehen, wie in dem 
Beispiel oben, wo ich einfach ->enableDevice(12, 42) schreiben kann 
statt die vorgesehen Konstanten zu verwenden. Ob man das mit Heimtücke 
und Aufwand austricksen kann ist egal, sowas meine ich nicht.

vlg
 Timm

von mh (Gast)


Lesenswert?

Timm R. schrieb:
> ...>
> Das ist meine Frage. Geht es, und wenn ja, wie, dass ich die Sache so
> gestalten kann, dass ich nur genau die vorgesehenen Konstanten übergeben
> kann und nicht irgendwas konvertierbares?
>
> Mit "nicht kann" meine ich so beiläufig oder aus Versehen, wie in dem
> Beispiel oben, wo ich einfach ->enableDevice(12, 42) schreiben kann
> statt die vorgesehen Konstanten zu verwenden. Ob man das mit Heimtücke
> und Aufwand austricksen kann ist egal, sowas meine ich nicht.
>
> vlg
>  Timm

Das was du suchst nennt sich strong type. Einen Einstieg ins Thema gibts 
z.B. hier 
https://www.fluentcpp.com/2016/12/08/strong-types-for-strong-interfaces/

von Carl D. (jcw2)


Lesenswert?

Verwende statt Konstanten eine
1
enum class DeviceDirection {
2
  Input, Output,
3
}
dann hilft der Compiler gegen "42"

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Carl D. schrieb:
> enum

Ja, das ist exakt für sowas gemacht.

Timm R. schrieb:
> Ob man das mit Heimtücke und Aufwand austricksen kann ist egal, sowas
> meine ich nicht.

Das freut mich zu hören, denn viele übertreiben es maßlos, indem sie 
versuchen, ihre API gegen gezielte Sabotage abzusichern statt nur gegen 
versehentliche Falschbenutzung.

von Ralf C. (Gast)


Lesenswert?

Rolf M. schrieb:
> Carl D. schrieb:
>> enum
>
> Ja, das ist exakt für sowas gemacht.

Ergänzung einer Ungenauigkeit:

In diesem Fall möchte er TO explizit eine starke Typisierung.
Mit dem von dir zitiertem Ausschnitt könnte man denken, dass ein "enum" 
genügen würde.
Ein einfacher enum (unscoped enum) hat implizite Konvertierungen 
von/nach int. Im ursprünglichen Beitrag von Carl D. stand aber "enum 
class", also  ein scoped enum. Diese haben keine impliziten 
Konvertierungen mit int und können nur über den Namen (oder explizite 
Konvertierungen) verwendet werden.

Und das ist, was der TO verwenden möchte.

https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations

von Timm R. (Firma: privatfrickler.de) (treinisch)


Lesenswert?

Hallo,

vielen Dank an euch alle! Sehr hilfreich und konkret und das Problem ist 
damit gelöst.

Viele liebe Grüße
Timm

von Rolf M. (rmagnus)


Lesenswert?

Ralf C. schrieb:
> Rolf M. schrieb:
>> Carl D. schrieb:
>>> enum
>>
>> Ja, das ist exakt für sowas gemacht.
>
> Ergänzung einer Ungenauigkeit:
>
> In diesem Fall möchte er TO explizit eine starke Typisierung.
> Mit dem von dir zitiertem Ausschnitt könnte man denken, dass ein "enum"
> genügen würde.
> Ein einfacher enum (unscoped enum) hat implizite Konvertierungen
> von/nach int.

Das ist falsch!
Beim Versuch, folgendes C++-Programm zu übersetzen:
1
enum DeviceDirection
2
{
3
    InputDevice = 0,
4
    OutputDevice = 1
5
};
6
7
void enableDevice(int index, DeviceDirection dir)
8
{
9
}
10
11
int main()
12
{
13
    enableDevice(12, 42);
14
}
meldet der Compiler:
1
enum.cpp: In function ‘int main()’:
2
enum.cpp:13:24: error: invalid conversion from ‘int’ to ‘DeviceDirection’ [-fpermissive]
3
     enableDevice(12, 42);
4
                        ^
5
enum.cpp:7:6: note:   initializing argument 2 of ‘void enableDevice(int, DeviceDirection)’
6
 void enableDevice(int index, DeviceDirection dir)
7
      ^~~~~~~~~~~~

Das ist exakt das, was der TO beschrieben hat, was passieren soll.

> Im ursprünglichen Beitrag von Carl D. stand aber "enum
> class", also  ein scoped enum. Diese haben keine impliziten
> Konvertierungen mit int

Einen klassischen enum kann man nur nach int konvertieren, nicht von 
int.

von Ralf C. (Gast)


Lesenswert?

Rolf M. schrieb:
> Einen klassischen enum kann man nur nach int konvertieren, nicht von
> int.

Gut.

Der TO sei darauf hingewiesen, dass er sich aussuchen kann ob implizite 
Konvertierungen von enum nach int zulässig sein sollen (unscoped enum), 
oder nicht (scoped enum):
1
enum       Foo { A = 0, };
2
enum class Bar { B = 0, };
3
4
void fun()
5
{
6
  Foo::A + 42;  // ok
7
  Bar::B + 42;  // error: no match for ‘operator+’ (operand types are ‘Bar’ and ‘int’)
8
}

von Timm R. (Firma: privatfrickler.de) (treinisch)


Lesenswert?

Hallo,

ich erinnere mich. Von enums hatte ich mich für mich persönlich 
verabschiedet, weil mir überhaupt nicht gefiel, dass die Bezeichner aus 
dem enum in den umgebenden Scope durchsickern.

Das hat natürlich den Vorteil das die Bezeichner für die Konstanten 
eleganter sind.

Der scoped enum gefiel mir früher (TM) nicht, weil mir die Bezeichner 
dadurch zu gesprächig waren, aber ich muss sagen, heute gefällt mir das 
eigentlich sogar:
1
MidiManager::DeviceDirection::Input

Sieht doch eigentlich ganz nett aus. Ich werde also den enum class 
verwenden.

Danke noch mal

vlg
 Timm

von Rolf M. (rmagnus)


Lesenswert?

Timm R. schrieb:
> ich erinnere mich. Von enums hatte ich mich für mich persönlich
> verabschiedet, weil mir überhaupt nicht gefiel, dass die Bezeichner aus
> dem enum in den umgebenden Scope durchsickern.

> Der scoped enum gefiel mir früher (TM) nicht, weil mir die Bezeichner
> dadurch zu gesprächig waren, aber ich muss sagen, heute gefällt mir das
> eigentlich sogar

Ja, das ist zu einem gewissen Grad auch Geschmackssache. Ich bin ja auch 
nicht generell gegen enum class. Ich mag nur so lange Bandwurmnamen 
nicht so arg - finde sie schwerer zu erfassen, und der zusätzliche 
Bestandteil DeviceDirection:: gibt mit keine Information, die ich ohne 
ihn nicht hätte.

: Bearbeitet durch User
von Timm R. (Firma: privatfrickler.de) (treinisch)


Lesenswert?

Hallo Rolf,

Rolf M. schrieb:
> finde sie schwerer zu erfassen,

ja das stimmt. Vielleicht stören sie mich heute deswegen nicht mehr so?

Damals (TM) war ich einfach krasser drauf. Lange Sessions und Hauptsache 
die Tasten klappern :-)

Ich habe aber auch den Fluch, dass ich echt viel hin- und herschalten 
muss zwischen verschiedenen Sprachen, vielen macht das nichts, mir 
schon. Ich komme total schnell wieder raus und verwechsle Idiome etc.

Da können lange und selbsterklärende Namen schonmal eine echte Hilfe 
sein :-)

> und der zusätzliche
> Bestandteil DeviceDirection:: gibt mit keine Information, die ich ohne
> ihn nicht hätte.

Aber geiler Punkt! Danke! Ich denke ich werde das Direction rausnehmen! 
Das ist wirklich über. Das Device hilft mir aber.

vlg
Timm

von Ben W. (ben_w)


Lesenswert?

stichwort strong types wurde ja schon genannt.
die BOOST lib bietet da z.B. einen workarround

mit
BOOST_STRONG_TYPEDEF(int, DeviceDirection)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.