Kompilierzeitzeichenfolgen in einer Vorlage zur Kompilierzeit verketten?

Lesezeit: 8 Minuten

Benutzer-Avatar
Steven Lu

Aktuell habe ich:

template <typename T> struct typename_struct<T*> {
    static char const* name() { 
        return (std::string(typename_struct<T>::name()) + "*").c_str(); 
    }
};

Ich frage mich, ob ich das ganze Bit vermeiden kann, wo ich gezwungen bin, eine Zeichenfolge zuzuweisen, um die Verkettung durchzuführen.

Dies geschieht alles zur Kompilierzeit, dh ich beabsichtige, die Zeichenfolge zu erhalten "int****" wenn ich referenziere typename_struct<int****>::name(). (Gehen Sie davon aus, dass ich eine entsprechende Spezialisierung für deklariert habe int die zurückkehrt "int")

Wenn der Code jetzt geschrieben ist, führt der Compiler die Verkettung mit std::string nur während der Kompilierzeit durch? (Ich wäre damit einverstanden) Oder führt ein solcher Aufruf zur Laufzeit zu 4 std::string-basierten Verkettungen? (damit wäre ich nicht einverstanden)

  • Noch besser wäre es, das Bit zu vermeiden, in dem Sie einen ungültigen Zeiger (auf freigegebenen Speicher) zurückgeben.

    – Mike Seymour

    16. Juli 2014 um 14:32 Uhr

  • @MikeSeymour Guter Punkt. Es scheint, als würde man den Rückgabetyp auf setzen char const* ist einfach nicht praktikabel

    – Steven Lu

    16. Juli 2014 um 14:46 Uhr

  • std::string verwendet dynamischen Speicher, daher kann ich mir keinen Grund vorstellen, dies beim Kompilierungstyp zu tun, insbesondere nicht für die Leistung.

    – Neil Kirk

    16. Juli 2014 um 16:50 Uhr

  • @NeilKirk der Punkt ist, dass ich es nicht verwenden muss std::string

    – Steven Lu

    16. Juli 2014 um 17:39 Uhr

  • Einen echten C++-String-Typ zur Kompilierzeit zu haben, wäre ein großartiges Werkzeug, das man im Standard haben könnte, und es ist machbar, aber es ist noch nicht passiert. Diese neue Vorschlag ist aber interessant.

    – hellseherisch

    16. Juli 2014 um 22:09 Uhr

Benutzer-Avatar
pw

So etwas könntest du gebrauchen. Alles geschieht zur Kompilierzeit. Spezialisieren Sie base_typename_struct, um Ihre primitiven Typen zu definieren.

template <const char* str, int len, char... suffix>
struct append {
  static constexpr const char* value() {
    return append<str, len-1, str[len-1], suffix...>::value();
  }
};

template <const char* str, char... suffix>
struct append<str, 0, suffix...> {
  static const char value_str[];
  static constexpr const char* value() {
    return value_str;
  }
};

template <const char* str, char... suffix>
const char append<str, 0, suffix...>::value_str[] = { suffix..., 0 };


template <typename T>
struct base_typename_struct;

template <>
struct base_typename_struct<int> {
  static constexpr const char name[] = "int";    
};


template <typename T, char... suffix>
struct typename_struct {
  typedef base_typename_struct<T> base;
  static const char* name() {
    return append<base::name, sizeof(base::name)-1, suffix...>::value();
  }
};

template <typename T, char... suffix>
struct typename_struct<T*, suffix...>:
  public typename_struct<T, '*', suffix...> {
};


int main() {
  cout << typename_struct<int****>::name() << endl;
}

  • Ich denke, es kann eine Möglichkeit geben, automatisch zu drehen int hinein 'i','n','t' mithilfe eines Makros. Vielen Dank

    – Steven Lu

    16. Juli 2014 um 15:19 Uhr


  • Das ist großartig, aber ich denke, es ist begrenzt, weil es nicht möglich ist, constexpr-Strings zu verketten, es erlaubt nur das Anhängen einzelner Zeichen. Irgendwelche Ideen, um es etwas mächtiger zu machen, damit ich beliebige Concats machen kann? Marcos Link scheint eine Spur zu sein. Ich glaube, dass es möglich ist, ein Makro zu haben, das (natürlich zur Kompilierzeit) einen Unterindex von a abruft char* String … was wir brauchen, ist etwas, das dauert "int" hinein 'i','n','t'

    – Steven Lu

    16. Juli 2014 um 17:59 Uhr


  • Der spezifische Anwendungsfall wäre zB das Erhalten des Strings “int* Array” aus dem Typ int*[]

    – Steven Lu

    16. Juli 2014 um 18:01 Uhr

  • @StevenLu, du kannst es mit einem Makro machen: EXPAND("int") => 'i', 'n', 't'. Verwenden BOOST_PP_REPEAT mit irgendeiner Art PushBack<str, c> macht den Trick. Ausarbeitung

    – Chris

    16. Juli 2014 um 18:10 Uhr


  • Mit verallgemeinert constexpr Funktionen, wird es praktikabler, a zu verwenden constexpr Klasse, die bei Änderung neue Instanzen zurückgibt (a la reine Funktionen).

    – Chris

    16. Juli 2014 um 18:16 Uhr

Benutzer-Avatar
Hedede

Alternativer Weg ohne rekursive Templates (erfordert aber C++14):

#include <utility>
template<int...I> using is      = std::integer_sequence<int,I...>;
template<int N>   using make_is = std::make_integer_sequence<int,N>;

constexpr auto size(const char*s) { int i = 0; while(*s!=0){++i;++s;} return i; }

template<const char*, typename, const char*, typename>
struct concat_impl;

template<const char* S1, int... I1, const char* S2, int... I2>
struct concat_impl<S1, is<I1...>, S2, is<I2...>> {
    static constexpr const char value[]
    {
        S1[I1]..., S2[I2]..., 0
    };
};

template<const char* S1, const char* S2>
constexpr auto concat {
    concat_impl<S1, make_is<size(S1)>, S2, make_is<size(S2)>>::value
};

Beispiel:

constexpr const char a[] = "int";
constexpr const char c[] = "**";

#include <iostream>
int main()
{
    std::cout << concat<a,b> << '\n';
}

append Zeichen in Zeichenfolge können auch so implementiert werden, indem das zweite ersetzt wird const char* Parameter mit char....

  • Wenn ich constexpr auto a = “123” versuche; constexpr auto b = “456”; constexpr auto c = concat; Ich erhalte eine Fehlermeldung. Irgendeine Ahnung warum?

    – BioAbner J

    25. August 2021 um 2:55 Uhr

Benutzer-Avatar
Marco A.

Ich bin mir nicht sicher, wonach Sie suchen, aber ich glaube, Sie interessieren sich für eine Kombination aus Typ-ID und Namensentwirrung (welchen Compiler verwenden Sie?)

In gcc wäre es so etwas wie

#include<iostream>
#include <string>
#include <typeinfo>
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
using namespace std;

std::string demangle(const char* name) {
    int status = -4; // some arbitrary value to eliminate the compiler warning

    // enable c++11 by passing the flag -std=c++11 to g++
    std::unique_ptr<char, void(*)(void*)> res {
        abi::__cxa_demangle(name, NULL, NULL, &status),
        std::free
    };
    return (status==0) ? res.get() : name ;
}

template <typename T> struct typename_struct {
  static std::string name() {
    std::string typeName = typeid(T).name();
    return demangle(typeName.c_str());
  }
};

int main(){

  cout << typename_struct<int****>::name(); // Prints "int****"

  return 0;
}

http://ideone.com/nLsFF0

Quellen: https://stackoverflow.com/a/4541470/1938163

Zu deiner Frage: Das sind sie nicht constexpr Konstrukte, daher erfolgt die Auswertung zur Laufzeit, obwohl die Vorlagenparameter und der Code zur Kompilierzeit instanziiert werden.

Die Verwendung von Vorlagen bedeutet nicht, dass jede darin enthaltene Anweisung zur “Kompilierzeit” ausgeführt und aufgelöst wird.

Ich glaube, Sie können diese ganze Menge Zeug nicht zur Kompilierzeit erreichen, da De-Mangling-Funktionen (ABI-spezifisch) beteiligt sind. Wenn ich Ihre Frage falsch interpretiert habe, lassen Sie es mich bitte wissen.

  • Richtig, ich kenne mich mit Typeid und Name-Demanbling aus, ich mache das parallel (damit ich es überprüfen kann – es macht normalerweise die Typen von Template-Instanziierungen hässlich). In meinem Projekt hier geht es darum, “Reflexion” auf andere Weise zu erreichen, also versuche ich zu sehen, ob ich Vorlagen definieren kann, um alle Grundlagen abzudecken. Es hat bisher gut funktioniert. Jetzt sagen Sie, dass es sich nicht um constexpr-Konstrukte handelt, weil ich es verwende std::string um die Verkettung zu tun? Gibt es eine Möglichkeit für mich, die Verkettung zur Kompilierzeit zu erzwingen, wenn ich alle diese Vorlagen auch zu constexprs machen kann (wenn das überhaupt Sinn macht)?

    – Steven Lu

    16. Juli 2014 um 14:57 Uhr


  • Es ist eigentlich keine große Sache, wenn jede “Zeigerkette” zur Laufzeit eine lineare Zeichenfolgenverkettungsoperation induziert. Hauptsächlich, weil ich das nur für “Kompatibilität” brauche. Die Verwendung dieser verrückten Typen ist in fast allen Fällen nicht ratsam.

    – Steven Lu

    16. Juli 2014 um 15:00 Uhr

  • @StevenLu Es gibt mehrere Einschränkungen für constexpr Funktionen, schau mal hier: de.cppreference.com/w/cpp/language/constexpr . Das geht nicht mit std::stringkönnen Sie auch hier nach String-Verkettungen suchen: stackoverflow.com/a/9054709/1938163

    – Marco A.

    16. Juli 2014 um 15:02 Uhr


  • @StevenLu: Ohne Compiler-Unterstützung können Sie keine sinnvolle Reflektion durchführen.

    – Welpe

    16. Juli 2014 um 15:05 Uhr

  • @Marco Super!!!! Vielen Dank für die Links. @Puppy Nun, ich bin noch nicht fertig, aber wenn ich dir die Beweise dafür zeigen kann, werde ich es tun. C++11 hat eine nette Trickkiste. Es kann ausreichend sein. (Tipp. Epische Mengen an Präprozessor-Makros und Templates)

    – Steven Lu

    16. Juli 2014 um 15:07 Uhr


#include <iostream>

//
***************************************************************************
template<const char* S1, const char* S2, size_t I1 = 0, size_t I2 = 0, char = S1[I1], char = S2[I2], char... Chars>
struct Concat : Concat<S1, S2, I1 + 1, I2, S1[I1 + 1], S2[I2], Chars..., S1[I1]>
{
};

// ****************************************************************************
template<const char* S1, const char* S2, size_t I1, size_t I2, char C2, char... Chars>
struct Concat<S1, S2, I1, I2, 0, C2, Chars...> : Concat<S1, S2, I1, I2 + 1, 0, S2[I2 + 1], Chars..., S2[I2]>
{
};

// ****************************************************************************
template<const char* S1, const char* S2, size_t N1, size_t N2, char... Chars>
struct Concat<S1, S2, N1, N2, 0, 0, Chars...>
{
  static constexpr const char Text[] = { Chars... , 0 };
};

// ****************************************************************************
static constexpr const char A[] = "123";
static constexpr const char B[] = "456";

// ****************************************************************************
int main(int argc, char* argv[]){
  std::cout << Concat<A, B>::Text << std::endl;
  return 0;
}

1018810cookie-checkKompilierzeitzeichenfolgen in einer Vorlage zur Kompilierzeit verketten?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy