You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This works great for Integer, UInteger, Long and ULong... but the other types can't use the same "trick". More thinking needs to be done to determine if there is method where the same sort of code pattern can work on Byte, SByte, Char, Short, UShort, Single, Double, etc.
Sounds like a great place to start. Here's a couple of questions:
First, how important is thread safety? That's a big difference between x+=1 and using Threading.Interlocked.Increment. Without thread safety, it would be easy enough to implement for all the built-in numeric types. With thread safety, we can use Threading.Interlocked.Increment/Decrement for Integer, Long, UInteger, and ULong; we can write our own thread-safe code using Threading.Interlocked.CompareExchange for Single and Double; and I don't know about the rest.
Second (and this might go into bikeshedding territory), why one function instead of two? (Although, honestly, I would almost suggest three: a Sub Inc/Dec, a pre-applying Function PreInc/PreDec, and a post-applying Function PostInc/PostDec.)
Really, we don't need the Inc/Dec I suggested. I just like it because it separates "I need the old value", "I need the new value", and "I don't care because I'm just using this as a statement"; and it doesn't force the "I don't care" users to pick PreX or PostX. For the translation from C# scenario, it's probably unnecessary, but I thought I'd toss it in for completeness' sake.
Did a little code tinkering today, and here's a couple of methods. (I picked different names again, because naming is hard, and I kept forgetting which is which when it comes to prefix and postfix ++, let alone the method names I just posted a couple of days ago.)
<Extension>PublicFunctionIncAndGet(ByRefvalueAsInteger)AsIntegervalue+=1ReturnvalueEndFunction<Extension>PublicFunctionIncAndGet_ThreadSafe(ByRefvalueAsInteger)AsIntegerReturnInterlocked.Increment(value)EndFunction<Extension>PublicFunctionIncAndGet(ByRefvalueAsSingle)AsSinglevalue+=1ReturnvalueEndFunction<Extension>PublicFunctionIncAndGet_ThreadSafe(ByRefvalueAsSingle)AsSingle'Get local copy of valueDimoldValue=value'Get incremented valueDimnewValue=oldValue+1'Try to update value (fails if another thread changed it since we got oldValue)DoUntilInterlocked.CompareExchange(value,newValue,oldValue)=oldValue'Get local copy of value againoldValue=value'Get incremented value againnewValue=oldValue+1LoopReturnnewValueEndFunction
It's been years since I last tinkered with CompareExchange, so that code might not be the best, but I think it works and gets the basic principle across.
I wonder if making increment/decrement thread-safe here is overkill. As far as I can tell, in C#, ++ and -- aren't thread-safe; that's why the Interlocked class exists in the first place. We could do the same here: just implement the naïve version and push people to Interlocked if they need thread safety. On the other hand, Static locals do the work behind the scenes to be thread-safe, so maybe doing the work here is worthwhile.
Next, I may look to see what it would take to add an Object overload that looks for C# operator overloads for ++ and -- (besides reflection and the hidden method names op_Increment and op_Decrement). For that matter, I don't know any meaningful overloads for those operators even exist.
I'm not so sure that it's about "thread safety". Something about the Interlocked stuff manages to get things to work "like C#". Meaning that they provide the same behavior and result as the ++ and -- in C#.
Using an x++ in a loop uses the current value of x for the task at hand and upon completion of that task then increments it by 1.
Take the following code:
int value = 1;
System.Console.WriteLine($"({value}) value++ = {value++} ({value})");
The result is:
(1) value++ = 1 (2)
So you can see that value is 1 for the first printed result, is 1 for the second "result" and 2 for the final one. The value.ToString() occurs during the task at hand and is a 1 and after the task is completed, it is incremented to a 2 thus showing a 2 for the final task... all within the same line.
The Interlocked stuff is somehow allowing the same behavior (for a subset of types supported by it). So the goal is to have the "same" code (but in VB) produce the same output...
Dim value = 1
Console.WriteLine($"({value}) value++ = {value.Incr} ({value})")
The IncAndGet that you have above wouldn't produce the same output as the above (which uses the Interlocked stuff). As I said, there's some sort of Voodoo magic happening here...
If you utilize the IncAndGet the output would look like:
(1) value++ = 2 (2)
I was thinking along the same lines and ended up removing all of the overloads since they weren't working as I'd originally thought - but they were very similar to what you wrote.
So I'm wondering if the Interlocked.CompareExchange produces the same Voodoo results, will have to explore this more when I have time.
All of the methods in my last post, including the ones using Interlocked, were for ++value. (I figured putting all 8 of these methods in one post was overkill.)
Here are the ones I wrote for value++:
<Extension>PublicFunctionGetAndInc(ByRefvalueAsInteger)AsIntegerGetAndInc=valuevalue+=1EndFunction<Extension>PublicFunctionGetAndInc(ByRefvalueAsSingle)AsSingleGetAndInc=valuevalue+=1EndFunction<Extension>PublicFunctionGetAndInc_ThreadSafe(ByRefvalueAsInteger)AsIntegerGetAndInc_ThreadSafe=valueInterlocked.Increment(value)EndFunction<Extension>PublicFunctionGetAndInc_ThreadSafe(ByRefvalueAsSingle)AsSingle'Get local copy of valueDimoldValue=value'Get incremented valueDimnewValue=oldValue+1'Try to update value (if another thread hasn't just changed it)DoUntilInterlocked.CompareExchange(value,newValue,oldValue)=oldValue'Get local copy of value againoldValue=value'Get incremented value againnewValue=oldValue+1LoopReturnoldValueEndFunction
Just added all of this to my current implementation... so this concludes 80% of the "project". Now begins the last 20% of the project which will take 80% of the "effort". ;-)
Major questions:
Should these be extension methods? I think on the surface it sounds attractive; however, I think the (will have to review further) Framework Design Guidelines somewhat frown on extension methods for this particular problem space.
Where should this functionality exist in the context of the existing (or new) Community.VisualBasic namespace?
Naming (the most difficult of these questions).
Thread-safe an issue (or not)?
Any other potential questions that I'm not currently thinking of?
"AVOID frivolously defining extension methods, especially on types outside your library or framework.
If you do own source code of a type, consider using regular instance methods instead. If you don't own the type, but you want to add a method, be very careful. Liberal use of extension methods has the potential to clutter APIs with types that were not designed to have these methods.
Another thing to consider is that extension methods are a compile-time facility, and not all languages provide support for them. Callers using languages without extension method support will have to use the regular static method call syntax to call your extension methods.
There are, of course, scenarios in which extension methods should be employed. These are outlined in the guidelines that follow."
The following recommendation seem to mainly be about interfaces, generics and/or functionality that would be in a separate assembly due to assembly dependencies. Finally, one of the big issues as I see regarding extension methods for this type of thing is:
"AVOID defining extension methods on System.Object.
VB users will not be able to call such methods on object references using the extension method syntax, because VB does not support calling such methods. In VB, declaring a reference as Object forces all method invocations on it to be late bound (the actual member called is determined at runtime), while bindings to extension methods are determined as compile-time (early bound). For example:
// C# declaration of the extension method
public static class SomeExtensions{
static void SomeMethod(this object o){...}
}
' VB will fail to compile as VB.NET does not support calling extension
' methods on reference types as Object
Dim o as Object = ...
o.SomeMethod();
VB users will have to call the method using the regular static method call syntax.
SomeExtensions.SomeMethod(o)
Note that this guideline applies to other languages where the same binding behavior is present, or where extension methods are not supported." -- Page 188
There is another annotated comment from Anthony Moore that discusses how overusing of extension methods reminds him of a similar problem experienced in C++.
So it would appear to me that the recommendation is that these shouldn't be extension methods. Thoughts?
Part of the logic behind this is that I'd like to keep the methods as small/concise as possible so that (hopefully) the code will be inlined when compiled.
The "Inline" portion may be something that we adjust depending on whether or not it is in it's own namespace ("???.Inline"). So we could have the following:
I'm 50/50 on whether or not the Increment methods should have the word After or not. With that said, I think I'm leaning more to having them state After then not... but it's a very thin lean as I think that the most common behavior is value++ and/or value--.
I think most of those questions could each almost be a big repo-wide design issue on its own.
Microsoft.VisualBasic doesn't, as I recall, contain any extension methods, so these don't have to be extension methods, either. Also, it's easier and less breaking to add <Extension> later if we decide we want it than it is to remove it if we add it now and regret it. (Fun fact: I believe you sort of can create extension methods on Object or on a unrestricted generic T, and if you do, they will work for every data type--except for Object itself, due to the reason you found.)
As for where to put it, if these methods are meant for general use, we could put increment and decrement in the existing VBMath module, and assignment might fit in the existing Interaction module (Why? Because IIf and Switch are in there.) If these methods are mainly for converting C# code to VB, we could take a page from the Microsoft.VisualBasic.Compatibility.VB6 namespace and create a Community.VisualBasic.Compatibility.CSharp namespace and put an Inline module in there.
As for naming, while I suspect x++ is more common than ++x in general, I don't know which is more common when they are being used inline. Also, Interlocked.Increment is equivalent to ++x, so that would be a precedent to drop the word Before and leave After. I think it would be best to keep both somehow--if you're relying on the return value of this function, it would be best if you knew for certain what it was to avoid off-by-one errors. Again, it depends on if these are intended to be for general use or for converting C# code.
Also, I really don't have a strong opinion on Inc vs Incr vs Increment. There are a lot of three-letter functions in Microsoft.VisualBasic (Rnd, Fix, Int, Val, Str, Hex, Oct, and so on), so Inc and Dec would fit in with that, but any prefix/suffix we add ruins that anyway. Incr and Decr are a little more descriptive while still being short, but the full word is, of course, the least ambiguous. Once again, it depends on if these are intended to be for general use or for converting C# code.
Activity
gilfusion commentedon Dec 14, 2020
Sounds like a great place to start. Here's a couple of questions:
First, how important is thread safety? That's a big difference between
x+=1and usingThreading.Interlocked.Increment. Without thread safety, it would be easy enough to implement for all the built-in numeric types. With thread safety, we can useThreading.Interlocked.Increment/DecrementforInteger,Long,UInteger, andULong; we can write our own thread-safe code usingThreading.Interlocked.CompareExchangeforSingleandDouble; and I don't know about the rest.Second (and this might go into bikeshedding territory), why one function instead of two? (Although, honestly, I would almost suggest three: a
Sub Inc/Dec, a pre-applyingFunction PreInc/PreDec, and a post-applyingFunction PostInc/PostDec.)DualBrain commentedon Dec 16, 2020
Wanted to leave a quick comment here letting you know that I'm currently digesting these thoughts and goingt o explore CompareExchange further...
With that, I do have a quick follow-up question... Three? Wouldn't Inc/Dec be the same as PostInc/PostDec - or am I missing a set of scenarios?
gilfusion commentedon Dec 16, 2020
Really, we don't need the Inc/Dec I suggested. I just like it because it separates "I need the old value", "I need the new value", and "I don't care because I'm just using this as a statement"; and it doesn't force the "I don't care" users to pick PreX or PostX. For the translation from C# scenario, it's probably unnecessary, but I thought I'd toss it in for completeness' sake.
gilfusion commentedon Dec 17, 2020
Did a little code tinkering today, and here's a couple of methods. (I picked different names again, because naming is hard, and I kept forgetting which is which when it comes to prefix and postfix ++, let alone the method names I just posted a couple of days ago.)
It's been years since I last tinkered with
CompareExchange, so that code might not be the best, but I think it works and gets the basic principle across.I wonder if making increment/decrement thread-safe here is overkill. As far as I can tell, in C#, ++ and -- aren't thread-safe; that's why the
Interlockedclass exists in the first place. We could do the same here: just implement the naïve version and push people toInterlockedif they need thread safety. On the other hand,Staticlocals do the work behind the scenes to be thread-safe, so maybe doing the work here is worthwhile.Next, I may look to see what it would take to add an
Objectoverload that looks for C# operator overloads for ++ and -- (besides reflection and the hidden method namesop_Incrementandop_Decrement). For that matter, I don't know any meaningful overloads for those operators even exist.DualBrain commentedon Dec 24, 2020
I'm not so sure that it's about "thread safety". Something about the Interlocked stuff manages to get things to work "like C#". Meaning that they provide the same behavior and result as the ++ and -- in C#.
Using an x++ in a loop uses the current value of x for the task at hand and upon completion of that task then increments it by 1.
Take the following code:
The result is:
(1) value++ = 1 (2)
So you can see that value is 1 for the first printed result, is 1 for the second "result" and 2 for the final one. The value.ToString() occurs during the task at hand and is a 1 and after the task is completed, it is incremented to a 2 thus showing a 2 for the final task... all within the same line.
The Interlocked stuff is somehow allowing the same behavior (for a subset of types supported by it). So the goal is to have the "same" code (but in VB) produce the same output...
The IncAndGet that you have above wouldn't produce the same output as the above (which uses the Interlocked stuff). As I said, there's some sort of Voodoo magic happening here...
If you utilize the IncAndGet the output would look like:
(1) value++ = 2 (2)
I was thinking along the same lines and ended up removing all of the overloads since they weren't working as I'd originally thought - but they were very similar to what you wrote.
So I'm wondering if the Interlocked.CompareExchange produces the same Voodoo results, will have to explore this more when I have time.
gilfusion commentedon Dec 24, 2020
All of the methods in my last post, including the ones using Interlocked, were for
++value. (I figured putting all 8 of these methods in one post was overkill.)Here are the ones I wrote for
value++:So,
and
should both result in
(1) value++ = 1 (2)
while
and
should both result in
(1) ++value = 2 (2)
DualBrain commentedon Dec 28, 2020
This is awesome! I'll update what I've done to utilize this approach.
DualBrain commentedon Dec 28, 2020
Just added all of this to my current implementation... so this concludes 80% of the "project". Now begins the last 20% of the project which will take 80% of the "effort". ;-)
Major questions:
Any other potential questions that I'm not currently thinking of?
DualBrain commentedon Dec 28, 2020
"AVOID frivolously defining extension methods, especially on types outside your library or framework.
If you do own source code of a type, consider using regular instance methods instead. If you don't own the type, but you want to add a method, be very careful. Liberal use of extension methods has the potential to clutter APIs with types that were not designed to have these methods.
Another thing to consider is that extension methods are a compile-time facility, and not all languages provide support for them. Callers using languages without extension method support will have to use the regular static method call syntax to call your extension methods.
There are, of course, scenarios in which extension methods should be employed. These are outlined in the guidelines that follow."
-- Framework Design Guidelines 3rd Edition Page 184.
The following recommendation seem to mainly be about interfaces, generics and/or functionality that would be in a separate assembly due to assembly dependencies. Finally, one of the big issues as I see regarding extension methods for this type of thing is:
"AVOID defining extension methods on System.Object.
VB users will not be able to call such methods on object references using the extension method syntax, because VB does not support calling such methods. In VB, declaring a reference as Object forces all method invocations on it to be late bound (the actual member called is determined at runtime), while bindings to extension methods are determined as compile-time (early bound). For example:
VB users will have to call the method using the regular static method call syntax.
Note that this guideline applies to other languages where the same binding behavior is present, or where extension methods are not supported." -- Page 188
There is another annotated comment from Anthony Moore that discusses how overusing of extension methods reminds him of a similar problem experienced in C++.
So it would appear to me that the recommendation is that these shouldn't be extension methods. Thoughts?
DualBrain commentedon Dec 28, 2020
As for naming, I'm leaning to the following:
Part of the logic behind this is that I'd like to keep the methods as small/concise as possible so that (hopefully) the code will be inlined when compiled.
The "Inline" portion may be something that we adjust depending on whether or not it is in it's own namespace ("???.Inline"). So we could have the following:
I'm 50/50 on whether or not the Increment methods should have the word After or not. With that said, I think I'm leaning more to having them state After then not... but it's a very thin lean as I think that the most common behavior is value++ and/or value--.
gilfusion commentedon Dec 29, 2020
I think most of those questions could each almost be a big repo-wide design issue on its own.
Microsoft.VisualBasic doesn't, as I recall, contain any extension methods, so these don't have to be extension methods, either. Also, it's easier and less breaking to add
<Extension>later if we decide we want it than it is to remove it if we add it now and regret it. (Fun fact: I believe you sort of can create extension methods onObjector on a unrestricted genericT, and if you do, they will work for every data type--except forObjectitself, due to the reason you found.)As for where to put it, if these methods are meant for general use, we could put increment and decrement in the existing VBMath module, and assignment might fit in the existing Interaction module (Why? Because IIf and Switch are in there.) If these methods are mainly for converting C# code to VB, we could take a page from the Microsoft.VisualBasic.Compatibility.VB6 namespace and create a Community.VisualBasic.Compatibility.CSharp namespace and put an Inline module in there.
As for naming, while I suspect
x++is more common than++xin general, I don't know which is more common when they are being used inline. Also, Interlocked.Increment is equivalent to++x, so that would be a precedent to drop the word Before and leave After. I think it would be best to keep both somehow--if you're relying on the return value of this function, it would be best if you knew for certain what it was to avoid off-by-one errors. Again, it depends on if these are intended to be for general use or for converting C# code.Also, I really don't have a strong opinion on
IncvsIncrvsIncrement. There are a lot of three-letter functions in Microsoft.VisualBasic (Rnd, Fix, Int, Val, Str, Hex, Oct, and so on), so Inc and Dec would fit in with that, but any prefix/suffix we add ruins that anyway. Incr and Decr are a little more descriptive while still being short, but the full word is, of course, the least ambiguous. Once again, it depends on if these are intended to be for general use or for converting C# code.Echo-8-ERA commentedon Jan 10, 2021
What about types that overload op_Increment and op_Decrement?
DualBrain commentedon Feb 24, 2022
@Echo-8-ERA Do you have an example?
Echo-8-ERA commentedon Feb 25, 2022
I once wrote a fixed point math library which overloaded op_increment/decrement to add/subtract 1. Lost the code when I changed my OS.