Friday, 14 September 2018

Making Delphi operator overloads compatible with C++Builder

Delphi operator overloads

A few days ago, I made a simple test program to test my BigIntegers in C++Builder. The important part looked like this:

#include "Velthuis.BigIntegers.hpp"

int _tmain(int argc, _TCHAR* argv[])
{
    BigInteger a = 17;
    BigInteger b = "123";
    BigInteger c = a + b;

But that did not compile. I got the following error message for the line that did the addition:

[bcc32c Error] File13.cpp(16): invalid operands to binary expression ('Velthuis::Bigintegers::BigInteger' and 'Velthuis::Bigintegers::BigInteger')

Now, BigIntegers, as I defined them, have overloads for many arithmetic and relational operators. A sample:

    class operator Add(const Left, Right: BigInteger): BigInteger;
    class operator Subtract(const Left, Right: BigInteger): BigInteger;
    class operator Multiply(const Left, Right: BigInteger): BigInteger;

But taking a look in the (Delphi-)generated Velthuis.BigInteger.hpp file, I saw that these are declared as simple static functions with an _op_ prefix:

static BigInteger __fastcall _op_Addition(const BigInteger &Left, 
    const BigInteger &Right);
static BigInteger __fastcall _op_Subtraction(const BigInteger &Left, 
    const BigInteger &Right);
static BigInteger __fastcall _op_Multiply(const BigInteger &Left, 
    const BigInteger &Right);

Lines wrapped to make them more readable in this blog

So, to use them, you have to call them explicitly:

    BigInteger c = BigInteger::_op_Addition(a, b);

That is ugly, and unusual for someone used to C++'s operator overloading. It makes using BigIntegers pretty inconvenient. OK, I defined static backup functions for the arithmetic overloads, so in the case of BigIntegers, you can just as well use a more or less Java-style method call:

    BigInteger c = BigInteger::Add(a, b);

C++ operator overloads

I wanted my operator overloads to be usable as in the first code sample. I read up a bit on C++ operator overloading, and came up with the following solution:

inline BigInteger operator +(const BigInteger& left, const BigInteger& right) 
{ return BigInteger::Add(left, right); }

I added that line to the C++ file before the main function, and all of a sudden it compiled and even produced the right result. So I wrote a file called Velthuis.BigIntegers.operators.hpp, with lines similar to the above for all supported operator overloads and included that too:

#include "Velthuis.BigIntegers.hpp"
#include "Velthuis.BigIntegers.operators.hpp"

Ok, that worked. But I didn't find that solution very satisfactory. To use BigIntegers with overloaded operators, you had to include an additional header file. And I could not just include that file in the Velthuis.BigIntegers.hpp file, because that is generated by the Delphi compiler, and any re-generation would overwrite any edits made to it. So what to do?

Including new header in generated header

But then I remembered that there is something like {$HPPEMIT} in Delphi. That allows you to add extra lines of C++ code to a generated .hpp file. So I added:

{$HPPEMIT '#include "Velthuis.BigIntegers.operators.hpp"'}

But that did not compile at all! I looked at the generated .hpp file and saw that the line was added somewhere near the top, well before the declaration of BigInteger. But it had to be included near the bottom of the generated hpp. Fortunately, the online help shows that nowadays (according to Remy Lebeau, even since Delphi 2009 or 2010), you can add an END attribute to the directive:

{$HPPEMIT END '#include "Velthuis.BigIntegers.operators.hpp"'}

And now it was added somewhere near the bottom of the header, well after the declaration of BigInteger. Success! Now the code sample at the top of this post compiled and worked as expected.

Conclusion

To recap: To make Delphi-defined operator overloads usable in C++, you will have to

  • write C++ operator overload wrappers for them.
  • You can pack those in a header of its own
  • and then include that header in the generated header for your Delphi file using {$HPPEMIT END '#include "NameOfYourHeader"'}.

I added these changes to my GitHub repository DelphiBigNumbers.

Rudy Velthuis

3 comments:

  1. Note that the END parameter of HPPEMIT has been around since at least Delphi 2010, probably even 2009 (but not 2007).

    ReplyDelete
    Replies
    1. Thanks Remy. I must admit I never needed HPPEMIT before. This was the first time. And when I was wondering how I could put stuff at the end of the header, I found it in the docs. So reading the docs really makes sense, sometimes.

      Delete
    2. Looks like my conversion helper tool and perhaps my article on conversions need an update.

      Delete