Hypercell ein ] Hypercell aus ] Zeige Navigation ] Verstecke Navigation ]
c++.net  
   

Die mobilen Seiten von c++.net:
https://m.c-plusplus.net

  
C++ Forum :: C++ (alle ISO-Standards) ::  Unique ID zur Compile Zeit generieren  
Gehen Sie zu Seite 1, 2, 3  Weiter
  Zeige alle Beiträge auf einer Seite
Auf Beitrag antworten
Autor Nachricht
Bergmann89
Mitglied

Benutzerprofil
Anmeldungsdatum: 29.04.2010
Beiträge: 62
Beitrag Bergmann89 Mitglied 17:14:17 12.09.2017   Titel:   Unique ID zur Compile Zeit generieren            Zitieren

Hallo Leute,

ich bin auf der Suche nach einer Möglichkeit zur Compile Zeit eine eindeutige ID für verschiedene Typen zu generieren. Folgenden Code habe ich bis jetzt:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
template<class... Args>
struct mp_unique_id
{
private:
    static void dummy() { }
 
public:
    static constexpr size_t value = (uint8_t*)&mp_unique_id::dummy - (uint8_t*)1;
};
 
using t_unique_id_1 = mp_unique_id<int, int   >;
using t_unique_id_2 = mp_unique_id<int, double>;


Das funktioniert auch soweit, wenn ich zur Laufzeit auf ::value zugreifen möchte. Ich kann es z.B. in der Console ausgeben. Wenn ich ::value aber als Parameter in ein Template stecken möchte, bricht der Compiler (g++ 6.3.0 20170406) mit einem Internal Error ab.
Gibt es noch andere Möglichkeiten eine eindeutige ID zur Compile Zeit zu generieren. Die IDs müssen dabei nur eindeutig innerhalb der aktuellen Translation Unit sein (was anderes würde eh nicht gehen).
Danke für die Hilfe :)

MfG Bergmann.
Techel
Mitglied

Benutzerprofil
Anmeldungsdatum: 19.09.2015
Beiträge: 812
Beitrag Techel Mitglied 18:19:55 12.09.2017   Titel:              Zitieren

Es gibt das __ COUNTER __-Makro, das in der aktuellen TU zu eindeutigen Werten expandiert. Ist nicht Standard, wird aber vom MSVC, GCC und Clang unterstützt.
Du kannst auch das _LINE_ Makro benutzen, ergibt aber nur für jede neue Zeile einen neuen Wert und kann durch #line überschrieben werden. Ich auch nen neuen Standardvorschlag unter cppreference.com gesehen, der __LINE__, __FILE etc. durch was besseres ersetzt. Kann ich aber gerade nicht finden.


Zuletzt bearbeitet von Techel am 18:20:27 12.09.2017, insgesamt 1-mal bearbeitet
Bergmann89
Mitglied

Benutzerprofil
Anmeldungsdatum: 29.04.2010
Beiträge: 62
Beitrag Bergmann89 Mitglied 19:47:00 12.09.2017   Titel:              Zitieren

Danke für den Tipp, aber den kannte ich schon und das hilft mir hier leider nicht weiter, weil ich für jede Template Spezialisierung eine extra ID haben möchte. Die Macros würden aber immer die selbe ID im Template erzeugen.
Arcoth
Moderator

Benutzerprofil
Anmeldungsdatum: 02.10.2013
Beiträge: 3577
Beitrag Arcoth Moderator 14:16:50 13.09.2017   Titel:              Zitieren

Techel schrieb:
Es gibt das __ COUNTER __-Makro, das in der aktuellen TU zu eindeutigen Werten expandiert. Ist nicht Standard, wird aber vom MSVC, GCC und Clang unterstützt.
Der Präprozessor hat doch überhaupt keinen Zugang zu dem Konzept einer Template-Instantiierung. Wenn wir verschiedenen Template-Instantiierungen verschiedene Zahlen zuweisen möchten, muss TMP Code her.

Und da dir TU-übergreifende Kompatibilität nicht wichtig ist:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <utility>
 
namespace detail {
  template <std::size_t N>
  struct    Rung { friend constexpr void rung(Rung<N>); };
  template <std::size_t N>
  struct AddRung { friend constexpr void rung(Rung<N>){} };
 
  template <std::size_t N, typename... Args>
  constexpr auto identify(bool) {
    (void)AddRung<N>{};
    return N;
  }
  template <std::size_t N, typename... Args,
            int = (rung(Rung<N>{}), 0)>
  constexpr auto identify(int) {
    return identify<N+1, Args...>(0);
  }
}
 
template <typename... Args>
constexpr auto Identifier = detail::identify<0, Args...>(0);

#include <iostream>

int main() {
  std::cout << Identifier<int> << Identifier<float> << Identifier<double>;
}
Funktioniert sowohl mit GCC als auch Clang unter C++14. Ich würde jedoch etwas anderes vorschlagen, denn diese Technik soll verboten werden; cf. core issue 2118. Warum nimmst du nicht einfach die Adresse der statischen Memberfunktion als ID? Die kannst du auch an Templateparameter vom Typ void(*)() übergeben. Wozu brauchst du konkrete Ganzzahlen?

_________________
Obstacles are those frightful things you see when you take your eyes off your goal. - Henry Ford


Zuletzt bearbeitet von Arcoth am 19:12:35 13.09.2017, insgesamt 1-mal bearbeitet
hustbaer
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 23750
Beitrag hustbaer Mitglied 14:32:12 13.09.2017   Titel:              Zitieren

Autsch ist das übel! :eek:
Finde ich gut wenn das verboten wird. Statt dessen könnte man ja einfach nen standardkonformen Weg machen z.B. Adressen von statischen Membern in nen (u)intptr_t zu konvertieren.

BTW: Weiss du vielleicht wieso das nicht erlaubt ist?

_________________
Until every person can enjoy all their human rights, we will not stop. I support Amnesty International. Will you?
https://www.amnesty.org / https://www.amnesty.de / https://www.amnesty.at
Arcoth
Moderator

Benutzerprofil
Anmeldungsdatum: 02.10.2013
Beiträge: 3577
Beitrag Arcoth Moderator 14:59:51 13.09.2017   Titel:              Zitieren

hustbaer schrieb:
BTW: Weiss du vielleicht wieso das nicht erlaubt ist?

Core issue 1384 schrieb:
CWG deemed that the complications of dealing with pointers whose tpes changed (pointer arithmetic and dereference could not be permitted on such pointers) outweighed the possible utility of relaxing the current restriction.
Dass man Zeiger nicht in Zahlen konvertieren kann, ist einfach eine Folge des Verbots von reinterpret_cast. Ich sehe auch nicht wirklich irgendeinen handfesten Anreiz, so eine Asymmetrie einzuführen. Die meisten Experten, vermute ich, würden auf die Strategie mit void(*)() als Typ deiner IDs hinweisen.

_________________
Obstacles are those frightful things you see when you take your eyes off your goal. - Henry Ford
Bergmann89
Mitglied

Benutzerprofil
Anmeldungsdatum: 29.04.2010
Beiträge: 62
Beitrag Bergmann89 Mitglied 15:36:42 13.09.2017   Titel:              Zitieren

Danke für den Code, ich probier das mal aus.

Ich brauch eine Zahl, weil ich aus dieser Zahl einen String generieren möchte (zur Compile Zeit) und dazu brenötige ich arithmetische Operationen, die auf Zeigern nicht erlaubt sind :/

€: dein Code geht nur wenn ich eine Ausnahme für "non-template-friend" rund rum mach
C++:
#pragma GCC diagnostic ignored "-Wnon-template-friend"


Zuletzt bearbeitet von Bergmann89 am 15:55:52 13.09.2017, insgesamt 1-mal bearbeitet
hustbaer
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 23750
Beitrag hustbaer Mitglied 19:38:01 13.09.2017   Titel:              Zitieren

@Arcoth
Ahh, ich doof. Mit weniger Kopfweh nochmal drüber nachdenken hilft.
Der schwerwiegendste Grund hat meiner Einschätzung nach nix damit zu tun dass rumgecastete Zeiger nicht constexpr dereferenziert werden könnten, und daher reinterpret_cast halt generell verboten ist. Das liesse sich leicht fixen indem man nur die Richtung int -> pointer verbietet.

Viel problematischer ist aber dass der Wert des Integers der dabei entstehen soll einfach nicht sinnvoll "compile time" ermittelt werden kann. Weil der im Prinzip erst beim Linken feststeht. Linken kann man aber erst wenn man weiss was das Programm überhaupt machen soll. Und das kann man ja sehr einfach von compile-time-konstanten Integerwerten abhängig machen.

Was dann auch gleich die Antwort darauf ist:

Arcoth schrieb:
Ich sehe auch nicht wirklich irgendeinen handfesten Anreiz, so eine Asymmetrie einzuführen. Die meisten Experten, vermute ich, würden auf die Strategie mit void(*)() als Typ deiner IDs hinweisen.

Naja mit dem void(*)() kannst du halt nix machen, ausser ihn als Template Parameter zu verwenden. Aber du kannst nicht drauf switchen, keinen Hashwert daraus ausrechnen - dergleichen Dinge. Und selbst wenn du ne if-Kaskade hinschreibst wir der Compiler diese nicht gut optimieren können, da er den Wert des Zeigers ja noch nicht kennt.

Und dann frage ich mich wozu ein void(*)() überhaupt noch gut sein soll, wenn alles was man damit anstellt eh erst zur Laufzeit ausgewertet werden kann. Dann kann man vermutlich genau so gut std::type_index nehmen. Bzw. wenn man some_key<T> in Templates als Key braucht, kann man wohl genau so gut gleich nur T als Key nehmen.

_________________
Until every person can enjoy all their human rights, we will not stop. I support Amnesty International. Will you?
https://www.amnesty.org / https://www.amnesty.de / https://www.amnesty.at
Arcoth
Moderator

Benutzerprofil
Anmeldungsdatum: 02.10.2013
Beiträge: 3577
Beitrag Arcoth Moderator 00:46:07 14.09.2017   Titel:              Zitieren

hustbaer schrieb:
@Arcoth
Ahh, ich doof. Mit weniger Kopfweh nochmal drüber nachdenken hilft.
Der schwerwiegendste Grund hat meiner Einschätzung nach nix damit zu tun dass rumgecastete Zeiger nicht constexpr dereferenziert werden könnten, und daher reinterpret_cast halt generell verboten ist. Das liesse sich leicht fixen indem man nur die Richtung int -> pointer verbietet.
SIcher, aber warum sollte man pointer -> int konkret erlauben? Die WG Ausschüsse ist grundsätzlich pessimistisch eingestellt, und führen nur die Parts ein, die für ein Feature wesentlich sind. Alles andere wird dann mit entsprechenden Papers erweitert. Das strukturiert den Gesamtprozess. Es gab bisher schlicht keine motion bzgl. dieser Konvertierung. Das ist aber hier nicht der Hauptgrund, der kommt hier:
Zitat:
Viel problematischer ist aber dass der Wert des Integers der dabei entstehen soll einfach nicht sinnvoll "compile time" ermittelt werden kann. Weil der im Prinzip erst beim Linken feststeht.
Du hast Recht, der Linker führt relocation durch. Es kann unter gewissen Architekturen auch relocation zur load time notwendig sein, weshalb einem Symbol allgemein vor der Ausführung des Programs keine sinnvolle Adresse zuweisen kann.
Aber das ist gar nicht nötig, denn schon wenn ein Zeiger via (int*)(intptr_t)ptr ausgedrückt wird, zeigt er nicht auf das Objekt, und kann nicht verwendet werden, um darauf zuzugreifen. Nicht einmal Vergleiche von zwei identisch hergeleiteten Zeigerwerten sind definiert: Weil diese Zeiger nicht nachweislich auf das selbe Objekt zeigen. Wo liegt also das Problem, zur compile time einen zufälligen Wert zuzuweisen, der für Objekte innerhalb einer TU konsistent ist? Oder ist meine Argumentation oben einfach nicht mit der Praxis kompatibel? Es kann gut sein, dass ein als reachable deklariertes Objekt dann auch durch einen solchen Ausdruck angefasst werden darf. Alles in allem ist die de facto Lage IMO in Ordnung.

Edit: Man kann Clang dazu überreden, einen solchen scheinbar zufälligen Wert zu erzeugen, indem man ausnutzt, dass die Zweige in einer conditional expression mit Bedingung __builtin_constant_p als constant expressions übernommen werden.

Was den Nutzen von Zeigern zur compile zeit anbelangt: Der TE hat ja gerade ein variadisches Template. Der Zeiger würde dann eben als eine Art Hash von beliebig vielen Typen fungieren. Es stimmt aber, dass man damit so gut wie nichts anstellen kann. Die nächste Frage wäre, was der TE mit diesem Mechanismus zu lösen versucht. XY halt.

_________________
Obstacles are those frightful things you see when you take your eyes off your goal. - Henry Ford


Zuletzt bearbeitet von Arcoth am 00:58:37 14.09.2017, insgesamt 1-mal bearbeitet
hustbaer
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 23750
Beitrag hustbaer Mitglied 08:11:49 14.09.2017   Titel:              Zitieren

Arcoth schrieb:
Nicht einmal Vergleiche von zwei identisch hergeleiteten Zeigerwerten sind definiert: Weil diese Zeiger nicht nachweislich auf das selbe Objekt zeigen.

Dafuq?
reinterpret_cast T* a -> uintptr_t i -> T* b ist soweit ich weiss sehrwohl definiert, und zwar so dass danach a == b gelten muss.
Oder meinst du den Fall T* a -> uintptr_t i -> U* b mit T != U?

Arcoth schrieb:
Wo liegt also das Problem, zur compile time einen zufälligen Wert zuzuweisen, der für Objekte innerhalb einer TU konsistent ist? Oder ist meine Argumentation oben einfach nicht mit der Praxis kompatibel? Es kann gut sein, dass ein als reachable deklariertes Objekt dann auch durch einen solchen Ausdruck angefasst werden darf. Alles in allem ist die de facto Lage IMO in Ordnung.

Angesichts der Tatsache dass der Aufwand es zu implementieren um mehrere Grössenordnungen zu hoch wäre, finde ich es auch OK. Was aber nicht heisst dass es nicht praktisch wäre :)


Arcoth schrieb:
Die nächste Frage wäre, was der TE mit diesem Mechanismus zu lösen versucht. XY halt.

Richtig. Das wäre interessant.

_________________
Until every person can enjoy all their human rights, we will not stop. I support Amnesty International. Will you?
https://www.amnesty.org / https://www.amnesty.de / https://www.amnesty.at
C++ Forum :: C++ (alle ISO-Standards) ::  Unique ID zur Compile Zeit generieren  
Gehen Sie zu Seite 1, 2, 3  Weiter
Auf Beitrag antworten

Zeige alle Beiträge auf einer Seite




Nächstes Thema anzeigen
Vorheriges Thema anzeigen
Sie können Beiträge in dieses Forum schreiben.
Sie können auf Beiträge in diesem Forum antworten.
Sie können Ihre Beiträge in diesem Forum nicht bearbeiten.
Sie können Ihre Beiträge in diesem Forum nicht löschen.
Sie können an Umfragen in diesem Forum nicht mitmachen.

Powered by phpBB © 2001, 2002 phpBB Group :: FI Theme

c++.net ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de Werbekostenerstattung verdient werden kann.

Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info und www.c-plusplus.net enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt (vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden. Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.