Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Support comparison operators#1347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
12 commits
Select commitHold shift + click to select a range
9a8cb96
Add comparison operators
christabellaDec 29, 2020
bcbace3
Add tests for logical (bool) comparison operators
christabellaDec 29, 2020
31f3ed1
Finish comparison operator impl and add tests
christabellaJan 6, 2021
ae7e774
Add eq and ineq operators, disallow reverse for comp
christabellaJan 7, 2021
5685727
Minor formatting (whitespace, ordering)
christabellaJan 7, 2021
03a73c0
Minor: use `out var` instead of declaring separately.
christabellaJan 7, 2021
67928e1
Implement .Equals and .GetHashCode to fix warning
christabellaJan 7, 2021
6a64678
Style (TryGetValue), formatting, and minor changes
christabellaJan 8, 2021
9dd9162
Use a separate map for comparison operators
christabellaJan 8, 2021
a8c6591
Add tuple comparison operator tests (2/6)
christabellaJan 11, 2021
6d4dec7
Add reverse case for tuple comparison op test
christabellaJan 11, 2021
7a2b5e2
Simplify PyObj casting, logic of if blocks.
christabellaJan 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 190 additions & 1 deletionsrc/embed_tests/TestOperator.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,17 @@ public class OperableObject
{
public int Num { get; set; }

public override int GetHashCode()
{
return unchecked(159832395 + Num.GetHashCode());
}

public override bool Equals(object obj)
{
return obj is OperableObject @object &&
Num == @object.Num;
}

public OperableObject(int num)
{
Num = num;
Expand DownExpand Up@@ -149,6 +160,103 @@ public OperableObject(int num)
return new OperableObject(a.Num ^ b);
}

public static bool operator ==(int a, OperableObject b)
{
return (a == b.Num);
}
public static bool operator ==(OperableObject a, OperableObject b)
{
return (a.Num == b.Num);
}
public static bool operator ==(OperableObject a, int b)
{
return (a.Num == b);
}

public static bool operator !=(int a, OperableObject b)
{
return (a != b.Num);
}
public static bool operator !=(OperableObject a, OperableObject b)
{
return (a.Num != b.Num);
}
public static bool operator !=(OperableObject a, int b)
{
return (a.Num != b);
}

public static bool operator <=(int a, OperableObject b)
{
return (a <= b.Num);
}
public static bool operator <=(OperableObject a, OperableObject b)
{
return (a.Num <= b.Num);
}
public static bool operator <=(OperableObject a, int b)
{
return (a.Num <= b);
}

public static bool operator >=(int a, OperableObject b)
{
return (a >= b.Num);
}
public static bool operator >=(OperableObject a, OperableObject b)
{
return (a.Num >= b.Num);
}
public static bool operator >=(OperableObject a, int b)
{
return (a.Num >= b);
}

public static bool operator >=(OperableObject a, PyObject b)
{
using (Py.GIL())
{
// Assuming b is a tuple, take the first element.
int bNum = b[0].As<int>();
return a.Num >= bNum;
}
}
public static bool operator <=(OperableObject a, PyObject b)
{
using (Py.GIL())
{
// Assuming b is a tuple, take the first element.
int bNum = b[0].As<int>();
return a.Num <= bNum;
}
}

public static bool operator <(int a, OperableObject b)
{
return (a < b.Num);
}
public static bool operator <(OperableObject a, OperableObject b)
{
return (a.Num < b.Num);
}
public static bool operator <(OperableObject a, int b)
{
return (a.Num < b);
}

public static bool operator >(int a, OperableObject b)
{
return (a > b.Num);
}
public static bool operator >(OperableObject a, OperableObject b)
{
return (a.Num > b.Num);
}
public static bool operator >(OperableObject a, int b)
{
return (a.Num > b);
}

public static OperableObject operator <<(OperableObject a, int offset)
{
return new OperableObject(a.Num << offset);
Expand All@@ -161,7 +269,7 @@ public OperableObject(int num)
}

[Test]
public voidOperatorOverloads()
public voidSymmetricalOperatorOverloads()
{
string name = string.Format("{0}.{1}",
typeof(OperableObject).DeclaringType.Name,
Expand DownExpand Up@@ -206,6 +314,24 @@ public void OperatorOverloads()

c = a ^ b
assert c.Num == a.Num ^ b.Num

c = a == b
assert c == (a.Num == b.Num)

c = a != b
assert c == (a.Num != b.Num)

c = a <= b
assert c == (a.Num <= b.Num)

c = a >= b
assert c == (a.Num >= b.Num)

c = a < b
assert c == (a.Num < b.Num)

c = a > b
assert c == (a.Num > b.Num)
");
}

Expand DownExpand Up@@ -263,6 +389,51 @@ public void ForwardOperatorOverloads()

c = a ^ b
assert c.Num == a.Num ^ b

c = a == b
assert c == (a.Num == b)

c = a != b
assert c == (a.Num != b)

c = a <= b
assert c == (a.Num <= b)

c = a >= b
assert c == (a.Num >= b)

c = a < b
assert c == (a.Num < b)

c = a > b
assert c == (a.Num > b)
");
}

[Test]
public void TupleComparisonOperatorOverloads()
{
string name = string.Format("{0}.{1}",
typeof(OperableObject).DeclaringType.Name,
typeof(OperableObject).Name);
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;
PythonEngine.Exec($@"
from {module} import *
cls = {name}
a = cls(2)
b = (1, 2)

c = a >= b
assert c == (a.Num >= b[0])

c = a <= b
assert c == (a.Num <= b[0])

c = b >= a
assert c == (b[0] >= a.Num)

c = b <= a
assert c == (b[0] <= a.Num)
");
}

Expand DownExpand Up@@ -304,6 +475,24 @@ public void ReverseOperatorOverloads()

c = a ^ b
assert c.Num == a ^ b.Num

c = a == b
assert c == (a == b.Num)

c = a != b
assert c == (a != b.Num)

c = a <= b
assert c == (a <= b.Num)

c = a >= b
assert c == (a >= b.Num)

c = a < b
assert c == (a < b.Num)

c = a > b
assert c == (a > b.Num)
");

}
Expand Down
34 changes: 34 additions & 0 deletionssrc/runtime/classbase.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,6 +21,7 @@ internal class ClassBase : ManagedType
[NonSerialized]
internal List<string> dotNetMembers;
internal Indexer indexer;
internal Dictionary<int, MethodObject> richcompare;
internal MaybeType type;

internal ClassBase(Type tp)
Expand All@@ -35,6 +36,15 @@ internal virtual bool CanSubclass()
return !type.Value.IsEnum;
}

public readonly static Dictionary<string, int> CilToPyOpMap = new Dictionary<string, int>
{
["op_Equality"] = Runtime.Py_EQ,
["op_Inequality"] = Runtime.Py_NE,
["op_LessThanOrEqual"] = Runtime.Py_LE,
["op_GreaterThanOrEqual"] = Runtime.Py_GE,
["op_LessThan"] = Runtime.Py_LT,
["op_GreaterThan"] = Runtime.Py_GT,
};

/// <summary>
/// Default implementation of [] semantics for reflected types.
Expand DownExpand Up@@ -72,6 +82,30 @@ public static IntPtr tp_richcompare(IntPtr ob, IntPtr other, int op)
{
CLRObject co1;
CLRObject co2;
IntPtr tp = Runtime.PyObject_TYPE(ob);
var cls = (ClassBase)GetManagedObject(tp);
// C# operator methods take precedence over IComparable.
// We first check if there's a comparison operator by looking up the richcompare table,
// otherwise fallback to checking if an IComparable interface is handled.
if (cls.richcompare.TryGetValue(op, out var methodObject))
{
// Wrap the `other` argument of a binary comparison operator in a PyTuple.
IntPtr args = Runtime.PyTuple_New(1);
Runtime.XIncref(other);
Runtime.PyTuple_SetItem(args, 0, other);

IntPtr value;
try
{
value = methodObject.Invoke(ob, args, IntPtr.Zero);
}
finally
{
Runtime.XDecref(args); // Free args pytuple
}
return value;
}

switch (op)
{
case Runtime.Py_EQ:
Expand Down
7 changes: 5 additions & 2 deletionssrc/runtime/classmanager.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -259,6 +259,7 @@ private static void InitClassBase(Type type, ClassBase impl)
ClassInfo info = GetClassInfo(type);

impl.indexer = info.indexer;
impl.richcompare = new Dictionary<int, MethodObject>();

// Now we allocate the Python type object to reflect the given
// managed type, filling the Python type slots with thunks that
Expand All@@ -284,6 +285,9 @@ private static void InitClassBase(Type type, ClassBase impl)
Runtime.PyDict_SetItemString(dict, name, item.pyHandle);
// Decref the item now that it's been used.
item.DecrRefCount();
if (ClassBase.CilToPyOpMap.TryGetValue(name, out var pyOp)) {
impl.richcompare.Add(pyOp, (MethodObject)item);
}
}

// If class has constructors, generate an __doc__ attribute.
Expand DownExpand Up@@ -553,8 +557,7 @@ private static ClassInfo GetClassInfo(Type type)
{
string pyName = OperatorMethod.GetPyMethodName(name);
string pyNameReverse = OperatorMethod.ReversePyMethodName(pyName);
MethodInfo[] forwardMethods, reverseMethods;
OperatorMethod.FilterMethods(mlist, out forwardMethods, out reverseMethods);
OperatorMethod.FilterMethods(mlist, out var forwardMethods, out var reverseMethods);
// Only methods where the left operand is the declaring type.
if (forwardMethods.Length > 0)
ci.members[pyName] = new MethodObject(type, name, forwardMethods);
Expand Down
7 changes: 4 additions & 3 deletionssrc/runtime/methodbinder.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -354,16 +354,17 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
int kwargsMatched;
int defaultsNeeded;
bool isOperator = OperatorMethod.IsOperatorMethod(mi);
int clrnargs = pi.Length;
// Binary operator methods will have 2 CLR args but only one Python arg
// (unary operators will have 1 less each), since Python operator methods are bound.
isOperator = isOperator && pynargs == clrnargs - 1;
isOperator = isOperator && pynargs == pi.Length - 1;
bool isReverse = isOperator && OperatorMethod.IsReverse((MethodInfo)mi); // Only cast if isOperator.
if (isReverse && OperatorMethod.IsComparisonOp((MethodInfo)mi))
continue; // Comparison operators in Python have no reverse mode.
if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList, out kwargsMatched, out defaultsNeeded) && !isOperator)
{
continue;
}
// Preprocessing pi to remove either the first or second argument.
bool isReverse = isOperator && OperatorMethod.IsReverse((MethodInfo)mi); // Only cast if isOperator.
if (isOperator && !isReverse) {
// The first Python arg is the right operand, while the bound instance is the left.
// We need to skip the first (left operand) CLR argument.
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp