Reputation: 2177
I'm currently building a BigInt class and, when overloading the operators &, | and ^, which will all have similar function syntax, I wondered if it would be possible to template the operators themselves as such:
template<operator K>
BigInt operator K( const BigInt& a, const BigInt& b )
{
BigInt Result = 0;
uint64_t Max = max(a.GetSize() , b.GetSize()) /* max(a,b) is defined outside */
for (uint64_t n=0; n < Max; n++)
{
Result[n] = a[n] K b[N];
}
return Result;
}
Where A[n] returns a bool with the nth digit of A (binary), and apply this to the operators &, | and ^, this way I wouldn't write 3 operator overloads that are identical for the exception of 2 letters.
I know this syntax doesn't work, but I'm asking if there is any way to do what you might expect this syntax to do: substitute K by &, | or ^ and only those when you write (a & b) in the code.
If it helps here is my definition of the class:
class BigInt
{
private:
vector<bool> num;
public:
/* Constructors */
BigInt();
template<class T> BigInt(T);
/* Class Methods */
void RLZ(); /* Remove Leading Zeroes */
uint64_t GetSize() const;
void print();
/* Operator Overloads */
std::vector<bool>::reference operator[] (uint64_t);
bool operator[] (uint64_t) const;
BigInt& operator=(const BigInt&);
};
Upvotes: 2
Views: 209
Reputation: 13589
One idea would be to define a helper function templated on the operator function (since you can't template on the operator itself), and then instantiate your template in the definitions of the appropriate BigInt operators. For example:
template <class OperatorFn>
BigInt bitwiseOperator(const BigInt& a, const BigInt& b) const
{
BigInt Result = 0;
uint64_t Max = max(a.GetSize(), b.GetSize());
for (uint64_t n=0; n < Max; n++)
{
Result[n] = OperatorFn{}(a[n], b[N]);
}
return Result;
}
BigInt operator&(const BigInt& a, const BigInt& b) const { return bitwiseOperator<std::bit_and<bool>>(a, b); }
BigInt operator|(const BigInt& a, const BigInt& b) const { return bitwiseOperator<std::bit_or<bool>>(a, b); }
BigInt operator^(const BigInt& a, const BigInt& b) const { return bitwiseOperator<std::bit_xor<bool>>(a, b); }
Upvotes: 7
Reputation: 275300
I would write this:
template<class F>
BigInt& bitwiseReplace( F&& f, BigInt& lhs, BigInt const& rhs ) {
auto max = (std::max)(lhs.GetSize(), rhs.GetSize());
for (uint64_t i=0; i < max; ++i)
lhs[i] = f( lhs[i], rhs[i] );
return lhs;
}
note that I take lhs
by reference and modify it. This is on purpose. This corresponds to &=
and |=
.
template<class F>
BigInt bitwiseCreate( F&& f, BigInt lhs, BigInt const& rhs ) {
bitwiseReplace( std::forward<F>(f), lhs, rhs );
return lhs;
}
this corresponds to +
or |
.
In your class:
BigInt& operator=(const BigInt&)=default;
BigInt& operator=(BigInt&&)=default;
BigInt(const BigInt&)=default;
BigInt(BigInt&&)=default;
have default move/copy assign/construct, as they do the right thing here.
Now you just have to write one line:
BigInt& operator&=( BigInt const& rhs ) { return bitwiseReplace( std::bit_and<bool>{}, *this, rhs ); }
BigInt& operator|=( BigInt const& rhs ) { return bitwiseReplace( std::bit_or<bool>{}, *this, rhs ); }
friend BigInt operator&( BigInt lhs, BigInt const& rhs ) { return bitwiseCreate( std::bit_and<bool>{}, std::move(lhs), rhs ); }
friend BigInt operator|( BigInt lhs, BigInt const& rhs ) { return bitwiseCreate( std::bit_or<bool>{}, std::move(lhs), rhs ); }
this requires a small amount of glue code.
The left hand side being taken by value means that
auto r = a | b | c;
is efficient -- the result of a | b
is moved (well actually elided) into (a|b) | c
with no copies performed.
Another advantage of this is that you can replace your std::vector<bool>
with a std::vector<uint32_t>
. Just write wordwiseReplace
and wordwiseCreate
that takes a function and operates on each uint32_t
in a very similar way.
You can with a bit of additional work even implement +
and -
this way. The function object can carry the carry information. You do have to check if there is overflow/underflow at the end and adjust for it.
*
and /
will require more work.
Upvotes: 5