A: 

I think you are using the std::hex in the wrong place:

#include <sstream>
#include <iostream>

int main()
{
    std::string     h("a5");
    std::stringstream   s(h);
    int x;

    s >> std::hex >> x;
    std::cout << "X(" << x << ")\n";
}
Martin York
A: 

when i do a cout int the parse function i get correct values but when i assign them to the uint8_t array they get messed up. i tried your suggestion it works in the function but they still get messed up when i assign them.

Hamza Yerlikaya
+1  A: 

Your problem may be in the output of the parsed data. The "<<" operator makes decisions on how to display data based on the data type passed it, and uint8_t may be getting interpretted as a char. Make sure you cast the array values to ints when printing, or investigate in a debugger.

The sample program:

uint8_t tgt_mac[6] = {0};
std::stringstream ss( "AA:BB:CC:DD:EE:11" );
char trash;

for ( int i = 0; i < 6; i++ )
{
    int foo;
    ss >> std::hex >> foo >> trash;
    tgt_mac[i] = foo;
    std::cout << std::hex << "Reading: " << foo << std::endl;
}

std::cout << "As int array: " << std::hex
    << (int) tgt_mac[0]
    << ":"
    << (int) tgt_mac[1]
    << ":"
    << (int) tgt_mac[2]
    << ":"
    << (int) tgt_mac[3]
    << ":"
    << (int) tgt_mac[4]
    << ":"
    << (int) tgt_mac[5]
    << std::endl;
std::cout << "As unint8_t array: " << std::hex
    << tgt_mac[0]
    << ":"
    << tgt_mac[1]
    << ":"
    << tgt_mac[2]
    << ":"
    << tgt_mac[3]
    << ":"
    << tgt_mac[4]
    << ":"
    << tgt_mac[5]
    << std::endl;

Gives the following output ( cygwin g++ )

Reading: aa
Reading: bb
Reading: cc
Reading: dd
Reading: ee
Reading: 11
As int array: aa:bb:cc:dd:ee:11
As unint8_t array: ª:»:I:Y:î:◄
Steven
+2  A: 

First I want to point out that I think @Steven's answer is a very good one - indeed I noticed the same: the values are correct, but the output looks awkward. This is due to ostream& operator<<( ostream&, unsigned char ) being used, since the uint8_t type you used is a typedef for unsigned char (as I found in the linux man pages). Note that on VC++, the typedef isn't there, and you have to use unsigned __int8 instead (which will also route you to the char specialization).

Next, you can test your code like this (output-independent):

assert( uint8_t( parseHex( "00" ) ) == uint8_t(0) );
assert( uint8_t( parseHex( "01" ) ) == uint8_t(1) );
//...
assert( uint8_t( parseHex( "ff" ) ) == uint8_t(255) );

In addition to Steven's answer, I just want to point out the existence of the transform algorithm, which could still simplify your code.

for( int j = 0 ; j < v.size() ; j++ ){
  tgt_mac[j] = parseHex(v.at(j)); 
}

Writes in one line:

std::transform( v.begin(), v.end(), tgt_mac, &parseHex );

(And I know that hasn't to do with the question...)

(See codepad.org for what it then looks like)

xtofl
A: 

I hate to answer this in this fashion, but sscanf() is probably the most succinct way to parse out a MAC address. It handles zero/non-zero padding, width checking, case folding, and all of that other stuff that no one likes to deal with. Anyway, here's my not so C++ version:

void
parse_mac(std::vector<uint8_t>& out, std::string const& in) {
    unsigned int bytes[6];
    if (std::sscanf(in.c_str(),
                    "%02x:%02x:%02x:%02x:%02x:%02x",
                    &bytes[0], &bytes[1], &bytes[2],
                    &bytes[3], &bytes[4], &bytes[5]) != 6)
    {
        throw std::runtime_error(in+std::string(" is an invalid MAC address"));
    }
    out.assign(&bytes[0], &bytes[6]);
}
D.Shawley