c++ सी++ में टाइप-दंड पर राय?




casting type-punning (4)

दक्षता की उपेक्षा करना, मैं जो कोड चाहता हूँ, उसकी सादगी के लिए:

#include <numeric>
#include <vector>
#include <cstring>

uint32_t compute_checksum(const char *data, size_t size) {
    std::vector<uint32_t> intdata(size/sizeof(uint32_t));
    std::memcpy(&intdata[0], data, size);
    return std::accumulate(intdata.begin(), intdata.end(), 0);
}

मैं भी लिटब के आखिरी उत्तर की तरह, जो प्रत्येक चार बारी में बदलाव करता है, सिवाय उसके बाद से चार हस्ताक्षर किए जा सकते हैं, मुझे लगता है कि इसे एक अतिरिक्त मुखौटा चाहिए:

checksum += ((data[i] && 0xFF) << shift[i % 4]);

जब प्रकार कबाड़ एक संभावित समस्या है, मैं इसे सुरक्षित रूप से करने की कोशिश की बजाय पल टाइप नहीं करना पसंद करते हैं यदि आप पहली जगह में अलग-अलग प्रकार के एलाइज पॉइंटर्स नहीं बनाते हैं, तो आपको चिंता करने की ज़रूरत नहीं है कि कंपाइलर उपनामों के साथ क्या कर सकता है, और न ही रखरखाव प्रोग्रामर जो एक यूनियन के माध्यम से आपके एकाधिक स्टैटिक कैम्स देखता है।

यदि आप इतनी अतिरिक्त मेमोरी आवंटित नहीं करना चाहते हैं, तो:

uint32_t compute_checksum(const char *data, size_t size) {
    uint32_t total = 0;
    for (size_t i = 0; i < size; i += sizeof(uint32_t)) {
        uint32_t thisone;
        std::memcpy(&thisone, &data[i], sizeof(uint32_t));
        total += thisone;
    }
    return total;
}

पर्याप्त अनुकूलन मेम्कपी और अतिरिक्त यूआईटी 32_टी वैल्यूएबल पूरी तरह जीसीसी से छुटकारा पायेगा, और सिर्फ एक सरणी के पूर्ण सरणी से बाहर, जो कि आपके प्लेटफ़ॉर्म पर जो कुछ भी सबसे प्रभावी तरीके से किया जाता है, एक पूर्णांक मूल्य पढ़ा। मुझे आशा थी कि वही अन्य "गंभीर" कंपाइलर के बारे में सच है लेकिन यह कोड अब लिटब के तुलना में बड़ा है, इसलिए इसके लिए कहा जाने के लिए बहुत कुछ नहीं है मेरे लिए एक फ़ंक्शन टेम्पलेट में बदलना आसान है जो कि यूआईटी 64_टी के साथ ही काम करेगा, और मेरा काम थोड़ा सा चुनने के बजाय मूल एंडियन-रास के रूप में होता है -endian।

यह बिल्कुल बिल्कुल पोर्टेबल नहीं है ऐसा लगता है कि sizeof (uint32_t) वर्ण का भंडारण प्रतिनिधित्व एक uin32_t के भंडारण प्रतिनिधित्व के अनुरूप है जिस तरह से हम चाहते हैं। यह प्रश्न द्वारा निहित है, क्योंकि यह बताता है कि एक को "दूसरे के रूप में माना जाता है" एन्डियन-नेस, चाहे एक चैर 8 बिट है, और क्या यूआईटी 32_t अपने स्टोरेज प्रतिनिधित्व में सभी बिट्स का उपयोग कर सकता है, जाहिर है, वह घुसपैठ कर सकता है, लेकिन सवाल यह दर्शाता है कि वे नहीं करेंगे।

मैं सी ++ में टाइप-पेनिंग पॉइंटर / एरे के लिए सम्मेलनों के बारे में उत्सुक हूँ इस समय मेरे पास उपयोग के मामले हैं:

32-बिट पूर्णांकों की एक सरणी के रूप में इसे इलाज के द्वारा डेटा के द्विआधारी ब्लॉब पर एक साधारण 32-बिट चेकसम की गणना करें (हम जानते हैं कि इसकी कुल लंबाई 4 का बहुविध है), और उसके बाद सभी मानों का संक्षेप करें और अतिप्रवाह की अनदेखी कर दें।

मुझे उम्मीद है कि ऐसे फ़ंक्शन इस तरह दिखेंगे:

uint32_t compute_checksum(const char *data, size_t size)
{
    const uint32_t *udata = /* ??? */;
    uint32_t checksum = 0;
    for (size_t i = 0; i != size / 4; ++i)
        checksum += udata[i];
    return udata;
 }

अब मेरे पास सवाल है, data को udata कनवर्ट करने के "सर्वोत्तम" तरीके पर आप क्या udata ?

सी शैली कास्ट?

udata = (const uint32_t *)data

सी ++ कास्ट मानता है कि सभी पॉइंटर्स परिवर्तनीय हैं?

udata = reinterpret_cast<const uint32_t *>(data)

सी ++ डाली कि मध्यवर्ती void* उपयोग से मनमानी पॉइंटर प्रकार के बीच?

udata = static_cast<const uint32_t *>(static_cast<const void *>(data))

एक संघ के माध्यम से कास्ट?

union {
    const uint32_t *udata;
    const char *cdata;
};
cdata = data;
// now use udata

मुझे पूरी तरह से एहसास है कि यह 100% पोर्टेबल समाधान नहीं होगा, लेकिन मैं इसे केवल एक छोटे से सेट प्लेटफॉर्म पर उपयोग करने की उम्मीद कर रहा हूं जहां मुझे पता है कि यह काम करता है (अर्थात् निरंतर स्मृति अभिगम और पॉइंटर एलियासिंग पर संकलक धारणा) आप क्या सुझाव देंगे?


मुझे पता है कि यह धागा थोड़ी देर के लिए निष्क्रिय रहा है, लेकिन मुझे लगता है कि मैं इस तरह की चीज़ों के लिए एक सामान्य जेनेरिक कास्टिंग दिनचर्या पोस्ट करूँगा:

// safely cast between types without breaking strict aliasing rules
template<typename ReturnType, typename OriginalType>
ReturnType Cast( OriginalType Variable )
{
    union
    {
        OriginalType    In;
        ReturnType      Out;
    };

    In = Variable;
    return Out;
}

// example usage
int i = 0x3f800000;
float f = Cast<float>( i );

आशा है कि यह किसी को मदद करता है!


यह reinterpret_cast का उपयोग करने के लिए केस-बुक उदाहरण की तरह दिखता है, और कुछ भी आपको इसके आधिकारिक उपयोग के लिए एक भाषा के निर्माण का उपयोग करने के बारे में जानकारी देने के बिना एक ही प्रभाव देगा।


मेरे पचास सेंट हैं - ऐसा करने के लिए अलग-अलग तरीके हैं।

#include <iostream>
#include <string>
#include <cstring>

    uint32_t compute_checksum_memcpy(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            // memcpy may be slow, unneeded allocation
            uint32_t dest; 
            memcpy(&dest,data+i,4);
            checksum += dest;
        }
        return checksum;
    }

    uint32_t compute_checksum_address_recast(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            //classic old type punning
            checksum +=  *(uint32_t*)(data+i);
        }
        return checksum;
    }

    uint32_t compute_checksum_union(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            //Syntax hell
            checksum +=  *((union{const char* c;uint32_t* i;}){.c=data+i}).i;
        }
        return checksum;
    }

    // Wrong!
    uint32_t compute_checksum_deref(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            checksum +=  *&data[i];
        }
        return checksum;
    }

    // Wrong!
    uint32_t compute_checksum_cast(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            checksum +=  *(data+i);
        }
        return checksum;
    }


int main()
{
    const char* data = "ABCDEFGH";
    std::cout << compute_checksum_memcpy(data, 8) << " OK\n";
    std::cout << compute_checksum_address_recast(data, 8) << " OK\n";
    std::cout << compute_checksum_union(data, 8) << " OK\n";
    std::cout << compute_checksum_deref(data, 8) << " Fail\n";
    std::cout << compute_checksum_cast(data, 8) << " Fail\n";
}




type-punning