क्या संकलन समय पर `` तार चार * `के दो तार सम्‍मिलित करना संभव है?


12

जाहिर है कि हम एक constexprफंक्शन में दो स्ट्रिंग लिटरल को कंसट्रेट कर सकते हैं , लेकिन एक स्ट्रिंग के साथ एक स्ट्रिंग शाब्दिक के कॉन्टेक्टेशन के बारे में क्या है जो किसी अन्य constexprफंक्शन द्वारा नीचे दिए गए कोड के समान है?

template <class T>
constexpr const char * get_arithmetic_size()
{
    switch (sizeof(T))
    {
    case 1: return "1";
    case 2: return "2";
    case 4: return "4";
    case 8: return "8";
    case 16: return "16";
    default: static_assert(dependent_false_v<T>);
    }
}

template <class T>
constexpr std::enable_if_t<std::is_arithmetic_v<T>, const char *> make_type_name()
{
    const char * prefix = std::is_signed_v<T> ? "int" : "uint";
    return prefix; // how to concatenate prefix with get_arithmetic_size<T>() ?
}

static_assert(strings_equal(make_type_name<int>, make_type_name<int32_t>);

कोड एक अंकगणितीय प्रकार के संकलक-स्वतंत्र स्ट्रिंग पहचानकर्ता बनाता है।

EDIT1:

थोड़ा और अधिक जटिल उदाहरण है:

template<typename Test, template<typename...> class Ref>
struct is_specialization : std::false_type {};

template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref> : std::true_type {};

template <class T>
constexpr std::enable_if_t<is_specialization<T, std::vector>::value || is_specialization<T, std::list>::value, const char *> make_type_name()
{
    return "sequence"; // + make_type_name<typename T::value_type>;
}

static_assert(strings_equal(make_type_name<std::vector<int>>(), make_type_name<std::list<int>>()));

2
बाइट युक्त एक std सरणी स्वीकार्य होगी? अन्यथा, आप ऐसा करने के लिए मैक्रोज़ और कोड जीन का उपयोग कर सकते हैं।
यक्क - एडम नेवरामोंट

@ यक-आदमनेवरुमोंट हाँ, ऐसा लगता है कि इससे बेहतर कोई समाधान नहीं है std::array(और शायद + वैरेडिक टेम्प्लेट)
दिमित्रिआनो

@ यक-आदमनेवरुमन्त सम्मति std :: arrays: stackoverflow.com/questions/42749032/… , सजीव उदाहरण: wandbox.org/permlink/VA85KCTqxiySrKE
दिमित्रिआनो

1
ऐसा प्रतीत होता है कि आप अनिवार्य रूप से किसी चीज़ को हाथ से रोल करने की कोशिश कर रहे हैं जो typeidऑपरेटर के समान परिणाम प्राप्त करता है । कारण typeidका हिस्सा लाइब्रेरी फ़ंक्शन के बजाय भाषा का हिस्सा है (उदाहरण के लिए समर्पित भाषा कीवर्ड द्वारा समर्थित) यह है कि इसे लागू करना "कंपाइलर मैजिक" पर निर्भर करता है - कार्यान्वयन के कुछ समर्पित समर्थन के बिना भाषा में लागू करना संभव नहीं है ।
पीटर

जवाबों:


4

यहाँ एक त्वरित संकलन समय स्ट्रिंग क्लास है:

template<std::size_t N>
struct ct_str
{
    char state[N+1] = {0};
    constexpr ct_str( char const(&arr)[N+1] )
    {
        for (std::size_t i = 0; i < N; ++i)
            state[i] = arr[i];
    }
    constexpr char operator[](std::size_t i) const { return state[i]; } 
    constexpr char& operator[](std::size_t i) { return state[i]; } 

    constexpr explicit operator char const*() const { return state; }
    constexpr char const* data() const { return state; }
    constexpr std::size_t size() const { return N; }
    constexpr char const* begin() const { return state; }
    constexpr char const* end() const { return begin()+size(); }

    constexpr ct_str() = default;
    constexpr ct_str( ct_str const& ) = default;
    constexpr ct_str& operator=( ct_str const& ) = default;

    template<std::size_t M>
    friend constexpr ct_str<N+M> operator+( ct_str lhs, ct_str<M> rhs )
    {
        ct_str<N+M> retval;
        for (std::size_t i = 0; i < N; ++i)
            retval[i] = lhs[i];
        for (std::size_t i = 0; i < M; ++i)
            retval[N+i] = rhs[i];
        return retval;
    }

    friend constexpr bool operator==( ct_str lhs, ct_str rhs )
    {
        for (std::size_t i = 0; i < N; ++i)
            if (lhs[i] != rhs[i]) return false;
        return true;
    }
    friend constexpr bool operator!=( ct_str lhs, ct_str rhs )
    {
        for (std::size_t i = 0; i < N; ++i)
            if (lhs[i] != rhs[i]) return true;
        return false;
    }
    template<std::size_t M, std::enable_if_t< M!=N, bool > = true>
    friend constexpr bool operator!=( ct_str lhs, ct_str<M> rhs ) { return true; }
    template<std::size_t M, std::enable_if_t< M!=N, bool > = true>
    friend bool operator==( ct_str, ct_str<M> ) { return false; }
};

template<std::size_t N>
ct_str( char const(&)[N] )->ct_str<N-1>;

आप इसे इस तरह से उपयोग कर सकते हैं:

template <class T>
constexpr auto get_arithmetic_size()
{
    if constexpr (sizeof(T)==1)
        return ct_str{"1"};
    if constexpr (sizeof(T)==2)
        return ct_str{"2"};
    if constexpr (sizeof(T)==4)
        return ct_str{"4"};
    if constexpr (sizeof(T)==8)
        return ct_str{"8"};
    if constexpr (sizeof(T)==16)
        return ct_str{"16"};
}

template <class T, std::enable_if_t<std::is_arithmetic<T>{}, bool> = true>
constexpr auto make_type_name()
{
    if constexpr (std::is_signed<T>{})
        return ct_str{"int"} + get_arithmetic_size<T>();
    else
        return ct_str{"uint"} + get_arithmetic_size<T>();
}

जो इस तरह के बयान की ओर जाता है:

static_assert(make_type_name<int>() == make_type_name<int32_t>());

गुजर।

जीवंत उदाहरण

अब एक कष्टप्रद बात यह है कि बफर की लंबाई प्रकार प्रणाली में है। आप एक lengthफ़ील्ड जोड़ सकते हैं , और N"बफर आकार" बना सकते हैं, और ct_strकेवल lengthअनुलिपि बाइट्स को कॉपी और छोड़ सकते हैं 0। फिर दोनों पक्षों common_typeकी अधिकतम वापसी के लिए ओवरराइड करें N

यह अनुमति देगा कि आप पास ct_str{"uint"}और ct_str{"int"}उसी प्रकार के मूल्य में करें और कार्यान्वयन कोड को थोड़ा कम कष्टप्रद बना दें।

template<std::size_t N>
struct ct_str
{
    char state[N+1] = {0};

    template<std::size_t M, std::enable_if_t< (M<=N+1), bool > = true>
    constexpr ct_str( char const(&arr)[M] ):
        ct_str( arr, std::make_index_sequence<M>{} )
    {}
    template<std::size_t M, std::enable_if_t< (M<N), bool > = true >
    constexpr ct_str( ct_str<M> const& o ):
        ct_str( o, std::make_index_sequence<M>{} )
    {}
private:
    template<std::size_t M, std::size_t...Is>
    constexpr ct_str( char const(&arr)[M], std::index_sequence<Is...> ):
        state{ arr[Is]... }
    {}
    template<std::size_t M, std::size_t...Is>
    constexpr ct_str( ct_str<M> const& o, std::index_sequence<Is...> ):
        state{ o[Is]... }
    {}
public:
    constexpr char operator[](std::size_t i) const { return state[i]; } 
    constexpr char& operator[](std::size_t i) { return state[i]; } 

    constexpr explicit operator char const*() const { return state; }
    constexpr char const* data() const { return state; }
    constexpr std::size_t size() const {
        std::size_t retval = 0;
        while(state[retval]) {
            ++retval;
        }
        return retval;
    }
    constexpr char const* begin() const { return state; }
    constexpr char const* end() const { return begin()+size(); }

    constexpr ct_str() = default;
    constexpr ct_str( ct_str const& ) = default;
    constexpr ct_str& operator=( ct_str const& ) = default;

    template<std::size_t M>
    friend constexpr ct_str<N+M> operator+( ct_str lhs, ct_str<M> rhs )
    {
        ct_str<N+M> retval;
        for (std::size_t i = 0; i < lhs.size(); ++i)
            retval[i] = lhs[i];
        for (std::size_t i = 0; i < rhs.size(); ++i)
            retval[lhs.size()+i] = rhs[i];
        return retval;
    }

    template<std::size_t M>
    friend constexpr bool operator==( ct_str lhs, ct_str<M> rhs )
    {
        if (lhs.size() != rhs.size()) return false;
        for (std::size_t i = 0; i < lhs.size(); ++i)
            if (lhs[i] != rhs[i]) return false;
        return true;
    }
    template<std::size_t M>
    friend constexpr bool operator!=( ct_str lhs, ct_str<M> rhs )
    {
        if (lhs.size() != rhs.size()) return true;
        for (std::size_t i = 0; i < lhs.size(); ++i)
            if (lhs[i] != rhs[i]) return true;
        return false;
    }
};

template<std::size_t N>
ct_str( char const(&)[N] )->ct_str<N-1>;

अब फ़ंक्शन कार्यान्वयन बन जाते हैं:

template <class T>
constexpr ct_str<2> get_arithmetic_size()
{
    switch (sizeof(T)) {
        case 1: return "1";
        case 2: return "2";
        case 4: return "4";
        case 8: return "8";
        case 16: return "16";
    }

}

template <class T, std::enable_if_t<std::is_arithmetic<T>{}, bool> = true>
constexpr auto make_type_name()
{
    constexpr auto base = std::is_signed<T>{}?ct_str{"int"}:ct_str{"uint"};
    return base + get_arithmetic_size<T>();
}

जो है एक बहुत अधिक लिखने के लिए प्राकृतिक।

जीवंत उदाहरण


ठंडा! यह उपयोग करने के लिए बेहतर है elseमें get_arithmetic_sizeसे if constexprभले ही आप कर return, क्योंकि बिना elseदावे dependent_false_v<T>विफल हो जाएगा।
दिमित्रिआनो

दूसरा विकल्प बेहद ठंडा है!
दिमित्रिआनो

4

नहीं, यह असंभव है। आप नीचे जैसा कुछ लागू कर सकते हैं (यह C ++ 14 है)।

#include <cmath>
#include <cstring>
#include <iostream>
#include <type_traits>

constexpr const char* name[] = {
  "uint1", "uint2", "uint4", "uint8", "uint16",
  "int1",  "int2",  "int4",  "int8",  "int16"
};

template <class T>
constexpr std::enable_if_t<std::is_arithmetic<T>::value, const char *> make_type_name() {
  return name[std::is_signed<T>::value * 5 +
    static_cast<int>(std::log(sizeof(T)) / std::log(2) + 0.5)];
}

static_assert(std::strcmp(make_type_name<int>(), make_type_name<int32_t>()) == 0);

int main() {
  std::cout << make_type_name<int>();
  return 0;
}

https://ideone.com/BaADaM

यदि आपको उपयोग करना पसंद नहीं है <cmath>, तो आप प्रतिस्थापित कर सकते हैं std::log:

#include <cstring>
#include <iostream>
#include <type_traits>

constexpr const char* name[] = {
  "uint1", "uint2", "uint4", "uint8", "uint16",
  "int1",  "int2",  "int4",  "int8",  "int16"
};

constexpr size_t log2(size_t n) {
  return (n<2) ? 0 : 1 + log2(n/2);
}

template <class T>
constexpr std::enable_if_t<std::is_arithmetic<T>::value, const char *> make_type_name() {
  return name[std::is_signed<T>::value * 5 + log2(sizeof(T))];
}

static_assert(std::strcmp(make_type_name<int>(), make_type_name<int32_t>()) == 0);

int main() {
  std::cout << make_type_name<int>();
  return 0;
}

std::logमेरे लिए बहुत जटिल है, तार को
छिपाने के

यह एक है constexpr, चिंता मत करो std::log()। आप इसे बदल सकते हैं, लेकिन कोड बड़ा हो जाएगा,
एसएम

क्या आपके पास EDIT1 के लिए एक उदाहरण है?
दिमित्रिआनो

4
मेरे ज्ञान का सबसे अच्छा करने के लिए, और न std::logही होने std::strcmpकी गारंटी है constexpr। वास्तव में, मानक विशेष रूप से उन्हें constexprC ++ 14 के बाद से प्रतिबंधित करता है । इसलिए, आपका कोड वास्तव में गैर-मानक एक्सटेंशन का उपयोग करता है।
एलएफ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.