How to fix warning CA2226 in a C++/CLI application? - c++-cli

I have a C++/CLI class definition where I'm trying to get Equality testing to be Value based rahter than Reference (similar to the behavior of String). The following definitions work:
namespace MyCode
{
public ref class MyClass
{
public:
MyClass();
bool operator==(MyClass^ obj) { return Equals(obj); }
bool operator!=(MyClass^ obj) { return !Equals(obj); }
virtual bool Equals(MyClass^ obj);
virtual bool Equals(System::Object^ obj) override;
virtual int GetHashCode() override;
};
}
However, my company is now requiring (and rightly so) that all code needs to conform to the Code Analysis rules. Code analysis consistently reports two warnings on the above class:
CA2226 : Microsoft.Usage : Since ''MyClass'' redefines operator '==', it should also redefine operator '!='.
CA2226 : Microsoft.Usage : Since ''MyClass'' redefines operator '!=', it should also redefine operator '=='.
The Microsoft documentation on warning CA2226 makes it clear that this is an important warning and should not be suppressed - but what else can I do?
I'm looking for a way (if possible) to 'fix' the code in order to remove this warning. Is that possible, or do I just need to suppress it?

For a ref class, you're supposed to implement operator==(MyClass^ left, MyClass^ right) as a static member function, this is the one other .NET languages will find.
Your current implementation defines operator==(MyClass%, MyClass^ right) instead, which is unusual.
Note that you can't rely on left != nullptr, you need to test ReferenceEquals(left, nullptr).

This is a .NET implementation detail. Having instance operator overloads is a C++ feature, the code analyzer chokes on it. The .NET way is to have operator overloads as static functions. Notably C# requires this. Solve your problem similar to this:
static bool operator==(MyClass^ lhs, MyClass^ rhs) { return lhs->Equals(rhs); }
static bool operator!=(MyClass^ lhs, MyClass^ rhs) { return !lhs->Equals(rhs); }

Related

How to force invoke method with object type input value without any type casting in a series of overloaded methods?

For example I'm having a class with three overloaded methods like this:
class MyClass
{
int sum(int i)
{
// Method implementation.
}
int sum(string x)
{
// Method implementation.
}
int sum(object o)
{
// Method implementation.
}
}
My question is when I call the sum method of MyClass by passing any value (integer, string or object) it should invoke only third method (with object type input parameter)
class MainClass
{
static void Main(string[] args)
{
MyClass obj = new MyClass();
obj.sum(10);
obj.sum("X")
}
}
You said "without type casting" but you can't, because you need some way to indicate to the compiler which version to call, and the runtime uses the type it sees to do that bit. Boxing the int as an object means the compiler will pick the object version
sum(1);//call int version
sum((object)1); //call object version
sum((string)(object)"1"); //call string version
sum((object)(int)(object)1); //call object version
First of all, let me say that if you sometimes want to call one version of the sum function when working with ints and sometimes want to call another, overloading probably isn't the right tool to use. Overloading works best when you are implementing conceptually the same operation for a number of different types, and you want the compiler to figure out automatically which function is the right one to call for each type; if you need more manual control over which function is called, you're probably better off using different names.
That said, if you're sure that this is what you want to do, you could implement the overloaded version for object in terms of another function in the public interface, as in:
class MyClass
{
int sum(int i)
{
// Method implementation.
}
int sum(string x)
{
// Method implementation.
}
int sum(object o)
{
sum_object(o);
}
int sum_object(object o)
{
// Method implementation for objects
}
}
Then, when you want to apply the object version to int and string objects, you just call sum_object directly instead.

Mixed C++/CLI overloading

Is this a proper restriction on overloading behavior. I can't figure out how to replicate a similar issue in pure C++ to compare behavior.
C++/CLI code:
class A {};
List<int>^ g(A &) {
return gcnew List<int>();
}
template<typename T>
List<T>^ g(T &) {
return gcnew List<T>();
}
void f() {
g(A()); // compiler error C3235
}
Generates:
error C3225: generic type argument for 'T' cannot be 'A', it must be a
value type or a handle to a reference type
The problem appears to be from the return values. It's requiring that the templated g() has a valid definition even though the non-templated g() overload should (shouldn't it?) be selected.

Equality operator, Equals, GetHashCode for const objects in C++ cli

I have created a new managed, reference class in C++ cli. And now I want to overload equality of objects of this class.
public ref class DerivedFromObject {
virtual bool Equals(Object^ obj) override;
virtual int GetHashCode() override;
static bool operator!= (const DerivedFromObject%, const DerivedFromObject%);
static bool operator== (const DerivedFromObject%, const DerivedFromObject%);
}
As far as I know from C#, operator!= calls operator==, that calls Equals, which refers to GetHashCode.
bool DerivedFromObject::operator==(const DerivedFromObject % a, const DerivedFromObject % b)
{
a.Equals((Object^)%b); // Error
}
In the snippet above, Equals and GetHashCode cannot be called on a const object, that's why I cannot call Equals from operator==.
How can I mark method that doesn't change the object in C++ cli? Or What the most appropriative way to define equality chain?
There's two things here:
.Net doesn't really have the concept of const in the way that C++ does. C++/CLI will enforce the rules that C++ has, but since .Net doesn't use it, no library methods are declared const, so you'll have problems calling just about any method. Rather than being explicitly declared like in C++, it's just by convention that methods like equals don't modify their parameters.
Since you used const%, I think you were trying to parallel the C++ convention of passing const& parameters. % is more similar in usage to ** double pointer than to &: You pass things parameters as % (or ^% for reference types) when the method can assign a value to the parameter, and it should be available to the calling method.
Here's how I would implement this:
public ref class DerivedFromObject : IEquatable<DerivedFromObject> {
virtual bool Equals(DerivedFromObject^ other);
virtual bool Equals(Object^ obj) override;
virtual int GetHashCode() override;
static bool operator!= (DerivedFromObject^, DerivedFromObject^);
static bool operator== (DerivedFromObject^, DerivedFromObject^);
}
One other thing you said:
...Equals, which refers to GetHashCode.
Be careful here, because it is possible for unequal objects to have the same hash code.
GetHashCode will need to evaluate everything, and then if the hash codes are equal, then Equals need to evaluate everything again to be sure they're actually equal. It'll probably be more efficient to not look at the hash code, just compare the objects field by field, and bail out as soon as they're not equal.

Should C++/CLI data members be handles or values?

I'm new to C++/CLI and I'm wondering what is "best practice" regarding managed type data members. Declaring as handle:
public ref class A {
public:
A() : myList(gcnew List<int>()) {}
private:
List<int>^ myList;
};
or as a value:
public ref class B {
private:
List<int> myList;
};
Can't seem to find definitive advice on this.
When writing managed C++ code, I'm in favor of following the conventions used by the other managed languages. Therefore, I'd go with handles for class-level data members, and only use values (stack semantics) where you'd use a using statement in C#.
If your class member is a value, then replacing the object entirely means that the object would need a copy constructor defined, and not many .NET classes do. Also, if you want to pass the object to another method, you'll need to use the % operator to convert from List<int> to List<int>^. (Not a big deal to type %, but easy to forget, and the compiler error just says it can't convert List<int> to List<int>^.)
//Example of the `%` operator
void CSharpMethodThatDoesSomethingWithAList(List<int>^ list) { }
List<int> valueList;
CSharpMethodThatDoesSomethingWithAList(%valueList);
List<int>^ handleList = gcnew List<int>();
CSharpMethodThatDoesSomethingWithAList(handleList);
It all depends on the lifetime. When you have a private member which lives exactly as long as the owning class, the second form is preferable.
Personally, I would use the second form. I say this because I use frameworks that are written by other teams of people, and they use this form.
I believe this is because it is cleaner, uses less space, and is easier for the non-author to read. I try to keep in mind that the most concise code, while still being readable by someone with minimal knowledge of the project is best.
Also, I have not encountered any problems with the latter example in terms of readability across header files, methods, classes, or data files ...etc
Though I'm FAR from an expert in the matter, that is what I prefer. Makes more sense to me.
class AlgoCompSelector : public TSelector {
public :
AlgoCompSelector( TTree *tree = 0 );
virtual ~AlgoCompSelector(){ /* */ };
virtual void Init(TTree *tree);
virtual void SlaveBegin(TTree *tree);
virtual Bool_t Process(Long64_t entry);
virtual void Terminate();
virtual Int_t Version() const { return 1; }
void setAlgo( Int_t idx, const Char_t *name, TTree* part2, TTree* part3 );
void setPTthres( Float_t val );
void setEthres( Float_t val );
private:
std::string mAlgoName[2]; // use this for the axis labels and/or legend labels.
TTree *mPart1;
TTree *mPart2[2], *mPart3[2]; // pointers to TTrees of the various parts
TBranch *mPhotonBranch[2]; // Used branches
TClonesArray *mPhotonArray[2]; // To point to the array in the tree
for example

Is it possible to create and use custom System::String^ extension methods in C++/CLI?

I tried making an extension to the built-in String class using C++/CLI, and using it from C++/CLI without success.
Here's the simplest I can boil it down to:
[System::Runtime::CompilerServices::Extension]
public ref class MyStringExtensions abstract sealed {
public:
[System::Runtime::CompilerServices::Extension]
static bool TestMethod(System::String^ str) { return false; }
};
Now, when I try to use this in other C++/CLI code, I get a compiler message indicating that TestMethod is not a method of String.
String^ foo = gcnew ...
...
blah = foo->TestMethod(); // compile-error
Any ideas?
C++ doesn't have extension methods.
But it does have ADL (Argument-dependent lookup, also known as Koenig lookup) which is arguably even nicer.