tags:

views:

143

answers:

3

I have this code:

    string get_md5sum(unsigned char* md) {
        char buf[MD5_DIGEST_LENGTH + MD5_DIGEST_LENGTH];
        char *bptr;
        bptr = buf;
        for(int i = 0; i < MD5_DIGEST_LENGTH; i++) {
                bptr += sprintf(bptr, "%02x", md[i]);
        }
        bptr += '\0';
        string x(buf);
        return x;
}

Unfortunately, this is some C combined with some C++. It does compile, but I don't like the printf and char*'s. I always thought this was not necessary in C++, and that there were other functions and classes to realize this. However, I don't completely understand what is going on with this:

 bptr += sprintf(bptr, "%02x", md[i]);

And therefore I don't know how to convert it into C++. Can someone help me out with that?

+4  A: 

sprintf returns number of bytes written. So this one writes to bptr two bytes (value of md[i] converted to %02x -> which means hex, padded on 2 chars with zeroes from left), and increases bptr by number of bytes written, so it points on string's (buf) end.

I don't get the bptr += '\0'; line, IMO it should be *bptr = '\0';

in C++ it should be written like this:

using namespace std;
stringstream buf;
for(int i = 0; i < MD5_DIGEST_LENGTH; i++)
{
    buf << hex << setfill('0') << setw(2) << static_cast<int>(static_cast<unsigned char>(md[i])); 
}
return buf.str();

EDIT: updated my c++ answer

Yossarian
I agree. It writes two bytes to bptr and advances bptr two bytes. The next line, `bptr += '\0';`, seems to make no sense. I think it adds 0 to bptr, instead of appending a nul character. What do you think about it?
Sjoerd
Oh, that was stupid.. I remembered the \0 from previous C and C++ books. I barely needed to use this because I usually work with libraries like Qt and .NET. I guess I added it somewhere to hope I was doing something to create a good char array.
henk
And do I really need to write two bytes each time or can I just write the whole array to a string at once? The unsigned char* is filled by openssl: unsigned char* tmp = MD5((unsigned char*) buffer, length, result);
henk
And by trying to compile your answer, I get this:md5.cpp: In function ‘std::string get_md5sum(unsigned char*)’:md5.cpp:24:25: error: no matching function for call to ‘fill(char)’md5.cpp:24:37: error: ‘width’ was not declared in this scope
henk
fill should be setfill, width should be setw, and the last argument needs some magic to convert it correctly to an int... Something like static_cast<int>(static_cast<unsigned char>( md[i] ) ); and you'll need the <sstream> and <iomanip> headers... This kind of ugliness with the streams is why I prefer my solution below.
Michael Anderson
Andreas Magnusson
Err, short* is my fault, corrected.
Yossarian
Thanks a lot for the explanations! I like both this answer and the one from Michael. At least I understand what is going on with this code. Thanks!
henk
`md[i]` is already an unsigned char, redundant cast. I would just write `int(md[i])`; C++ constructor-style cast.
MSalters
That works as well. Thanks!
henk
@MSalters, good catch I thought it was a char* argument. The cast to int or unsigned int is still necessary though.
Michael Anderson
@MSalters: Using the new style casts (`static_cast<>` etc) are preferable to ctor-style or C-style casts for the simple reason that you can always grep for them.
Andreas Magnusson
@Andreas Magnusson: By that logic, you'd have to write `static_cast<std::string>("foo")`. You should use static_cast when you need a non-trivial cast. Trivial casts (conversions), by virtue of being trivial, should not stand out and need not be greppable.
MSalters
@MSalters: Don't be ridiculous, we both know that errors introduced by type-casting are either related to pointer or numeric casting. In a project, who decides whether a cast is trivial or not? I've worked on enough projects with cargo cult programmers to know that simple rules are the best. Whenever an explicit cast is needed you should make it explicit enough to grep.
Andreas Magnusson
@Andreas Magnusson : I suggest that you read up on "cargo Cult". The core of that fallacy is that you believe that an absolute adherence to superficial ritual is sufficient to achieve the intended effect. Using `static_cast` religiously, and expecting that it magically fixes issues is a prime example of cargo cult programming. As for your statement that type-cast errors are due to either pointers or numeric casting, I'll attribute that to your lack of experience. I've seen more variants, such as type cast errors due to non-explicit constructors and templates(both not solvable by static_cast).
MSalters
@MSalters: I know what cargo cult means. The reason why `static_cast` is good is not that it automagically fixes everything, it's good because you are able to grep for casts that may be troublesome. If you force every programmer on your team to use `static_cast` (when applicable) that makes your job easier. Since you don't know me, there's no need for name-calling. I'm quite aware of other issues with casting but those are not relevant in the current context. I'm a bit disappointed that you feel the need to call me inexperienced in order to discredit my opinion.
Andreas Magnusson
@MSalters: Since you obviously need people to spell everything out for you my reference to cargo cult programmers is the following. Cargo cult programmers don't understand why things work so if you give them an option like 'use A in context X and B in context Y' they will be confused and start using A in context Y and vice versa. This is why it's much better to just say always use A if always using A is fine.
Andreas Magnusson
@Andreas Magnusson: I'm serious when I said that you should read up. Here, let me give you a link: http://en.wikipedia.org/wiki/Cargo_cult_programming . "Simple rules" are a giveaway that you're practicing cargo cult programming, as those simple rules are supposed to substitute for understanding.
MSalters
@MSalters: Yes, that's exactly what I'm saying. If you work with someone who does not quite understand what's going on, it's better to give them a simple rule, rather than having them confuse several rules and create their own bastard version of them. I've seen it happen on numerous occasions and the result isn't pretty. You can try to educate your peers but some people are not interested in improving, in that case simple rules are the best, then you can grep-and-fix afterward.
Andreas Magnusson
@MSalters: It seems that you feel you cannot debate against my opinion alone but you need to discredit me, either by implicating that I'm inexperienced or a cargo cult programmer of which I'm neither. Since you obviously cannot have a mature debate I don't think furthering this discussion will be beneficial to anyone.
Andreas Magnusson
+1  A: 
bptr += sprintf(bptr, "%02x", md[i]);

This is printing the character in md[i] as 2 hex characters into the buffer and advancing the buffer pointer by 2. Thus the loop prints out the hex form of the MD5.

bptr += '\0';

That line is probably not doing what you want... its adding 0 to the pointer, giving you the same pointer back...

I'd implememt this something like this.

string get_md5sum(unsigned char* md) {
        static const char[] hexdigits="0123456789ABCDEF";
        char buf[ 2*MD5_DIGEST_LENGTH ];
        for(int i = 0; i < MD5_DIGEST_LENGTH; i++) {
                bptr[2*i+0] = hexdigits[ md[i] / 16 ];
                bptr[2*i+1] = hexdigits[ md[i] % 16 ];
        }
        return string(buf,2*MD5_DIGEST_LENGTH );
}
Michael Anderson
A: 

I don't know C++, so without using pointers and strings and stuff, here's a (almost) pseudo-code for you :)

    for(int i = 0; i < MD5_DIGEST_LENGTH; i++) {
            buf[i*2] = hexdigits[(md[i] & 0xF0) >> 4];
            buf[i*2 + 1] = hexdigits[md[i] & 0x0F];
    }
pmg
Why post an almost identical (but less complete) solution as Michael? Why start an answer to a question specifically requesting a solution in C++ with 'I don't know C++'. Are you just hoping to get lucky?
Andreas Magnusson
I din't see Michael's answer when I posted mine, and there was no other answer without pointers (which seem to be problematic to the original poster) at the time I posted mine. And not knowing `C++` does not prevent one from explaining how a piece of `C` works.
pmg
Not knowing `C++` only prevents you from answering the original question, *'How do I convert this code to c++?'*. Anyway sorry about complaining that your solution is next to identical to Michael's, I guess the `hexdigits[]` made it look like a copy since it's not a name in the original code but it exists in Michael's.
Andreas Magnusson
No worries ... now that I think better about it, Michael had already supplied his answer and the hexdigit thing is a copy of his; but he had pointer arithmetic rather than indexes (`2*i+1`) in his code.
pmg