Compose an array with 2byte+1byte+2byte

Nov 23, 2012 at 10:17pm
Considering that an char* array, char* head = new char[5], must be filled with three values:

1
2
3
short start = 0xEB90;  // 2 bytes
char = 0x02;           // 1 byte
short lenght = 0;      // 2 bytes 

How to proceed in this endeavour?

Is it a good idea to do:

1
2
memset(header,'0xEB90',2);
header[2] = id;

But, how to add the last part (which was called lenght utilizing the short type just because it match with the size required)?

Thanks in advance.
Last edited on Nov 23, 2012 at 10:19pm
Nov 23, 2012 at 10:26pm
closed account (zb0S216C)
What you're trying to do is implementation-specific because the size of "short" can vary on different systems. It would be better if you did this:

 
char *head( new char[( ( 2 * sizeof( short ) ) + sizeof( char ) )] );

The above code will always guarantee that your array will hold 2 "short int"s and 1 "char". As for adding data into that array, you'd have to interpret an address within the array as a specific type then write to that address. For example:

1
2
3
4
5
6
7
8
9
10
union MultiPointer
{
    short *Short_;
    char *Byte_;
};

MultiPointer Pointer_;
Pointer_.Byte_ = head;
*Pointer_.Short_ = short( 1 );
*( Pointer_.Byte_ + sizeof( short ) ) = 'W';

Get the idea?

Wazzak
Nov 23, 2012 at 10:49pm
I think you meant to use memcpy instead of memset. It's a good idea.
The only thing you need is to keep track of the number of bytes you've written.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
char* head = new char[5];
short start = 0xEB90;
char mychar= 0x02;
short length = 0;
   
unsigned int WrittenBytes = 0;
   
memcpy( &head[WrittenBytes], &start, sizeof(start) );
WrittenBytes += sizeof(start);
    
memcpy( &head[WrittenBytes], &mychar, sizeof(mychar) );
WrittenBytes += sizeof(mychar);
    
memcpy( &head[WrittenBytes], &length, sizeof(length) );
WrittenBytes += sizeof(length);
Nov 23, 2012 at 11:28pm
Another idea:

1
2
3
4
5
6
7
typedef struct {
    short start;
    char id;
    short lenght;
} Head;
Head head1;
char *head = (char *)&head1


Is it a good approach?
Nov 23, 2012 at 11:52pm
Last edited on Nov 23, 2012 at 11:52pm
Nov 24, 2012 at 1:47am
C'mon. It's C++. Quit abusing unions and using C-style casts. Get creative!

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <stdexcept>
#include <memory>
#include <type_traits>
#include <iostream>
#include <iomanip>
#include <cstdint>
#include <limits>

class bytes
{
public:
    typedef std::uint8_t byte_type ;

    bytes(unsigned size) 
        : _size(size), _mem(new byte_type[size]) {}

    template <typename T>
    void store(unsigned index, T value, 
        typename std::enable_if<std::is_trivial<T>::value >::type* = 0)
    { 
        _check(index, sizeof(T)) ;
        *reinterpret_cast<T*>(&_mem[index]) = value ; 
    }

    template <typename T>
    T retrieve(unsigned index, 
        typename std::enable_if<std::is_trivial<T>::value >::type* = 0) const
    {
        _check(index, sizeof(T)) ;
        return *reinterpret_cast<T*>(&_mem[index]) ; 
    }

    unsigned size() const { return _size; }

    operator byte_type*() { return _mem.get(); }
    
    byte_type operator[](const unsigned index) const
    {
        _check(index, sizeof(_mem[0])) ;
        return _mem[index]; 
    }

private:

    void _check( unsigned index, unsigned size ) const
    {
        if ( index + size > _size )
            throw std::logic_error("bytes: Out of range!") ;
    }

    const unsigned _size ;
    std::unique_ptr<byte_type[]> _mem ;
};

std::int16_t start  = 0xEB90 ;
std::int16_t length = 0 ;
std::int8_t  id =  2 ;

const unsigned offset[] = { 0, sizeof(start), sizeof(start)+sizeof(id) } ;

int main()
{
    bytes myBytes(5) ;

   try 
   {
        myBytes.store(offset[0], start) ;
        myBytes.store(offset[1], id) ;
        myBytes.store(offset[2], length) ;
        // myBytes.store(offset[2], 39LL) ; // exception thrown

        std::cout << std::hex << myBytes.retrieve<int16_t>(offset[0]) << '\n' ;
        std::cout << static_cast<int>(myBytes.retrieve<int8_t>(offset[1])) << '\n' ;
        std::cout << myBytes.retrieve<int16_t>(offset[2]) << '\n' ;
   }   

   catch(std::exception& ex)
   {
       std::cerr << ex.what() << '\n' ;
   }
}

Nov 24, 2012 at 7:06pm
closed account (zb0S216C)
@cire: Please, tell me, what is not an abuse of "union"? </sarcasm > Also, you're code is good... if you like unnecessary complexity of a simple problem. Besides, the OP clearly stated that he/she will be using "short" and "char" as the target types; there's not need to bring templates into this.

Wazzak
Last edited on Nov 24, 2012 at 7:07pm
Nov 24, 2012 at 7:18pm
Please, tell me, what is not an abuse of "union"?


One might start with uses that aren't using undefined behavior to avoid a simple cast. </sarcasm>


Also, you're code is good... if you like unnecessary complexity of a simple problem.Besides, the OP clearly stated that he/she will be using "short" and "char" as the target types; there's not need to bring templates into this.


I know right. Who would design code for a general case instead of a specific case? I mean, it's pretty well known that code should never be reused and requirements never change.

Nov 24, 2012 at 7:21pm
requirements never change.


It's my experience that customers go to great lengths to help with this by ensuring they never actually provide any requirements in the first place. It they don't exist, they can't change :)
Nov 24, 2012 at 7:30pm
Pointer casting is a dangerous solution.
Some systems will crash if you dereference an address that is not properly aligned wrt its type.
Nov 24, 2012 at 8:09pm
That's a good point. I had in mind integral types and bit shifting in the original code, but that wouldn't work with arbitrary types, and I kind of threw that code out there. A correct implementation would have store and retrieve functions that looked more like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    template <typename T>
    void store(unsigned index, T value, 
        typename std::enable_if<std::is_trivial<T>::value >::type* = 0)
    { 
        _check(index, sizeof(T)) ;

        byte_type * source = reinterpret_cast<byte_type*>(std::addressof(value)) ;
        for ( unsigned i=0; i < sizeof(T) ; ++i )
            _mem[index+i] = *source++ ;
    }

    template <typename T>
    T retrieve(unsigned index, 
        typename std::enable_if<std::is_trivial<T>::value >::type* = 0) const
    {
        _check(index, sizeof(T)) ;

        T value ;
        byte_type * dest = reinterpret_cast<byte_type*>(std::addressof(value)) ;
        for ( unsigned i=0; i < sizeof(T) ; ++i )
            *dest++ = _mem[index+i] ;

        return value ;
    }


And certainly using memcpy in place of the for loops would work.
Topic archived. No new replies allowed.