www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Simple overloading without complications

reply Adam Sansier <Adam.Sansier gmail.com> writes:
I have a function that does some weird stuff, and can't really 
change it to make life easier(due to how windows work, COM, 
etc..).

The function normally takes a string, a name, and does its 
think(which is the complex part that I can't change).

But I also want to overload it so the function takes an int. I 
can't simply overload the function and call the string version or 
vice versa as one would normally do because this would require 
initializing twice, which can't happen because the way the code 
works.

The int value is a lookup into an array, and the name version 
searches the array for a name match. I can't overload the int 
version and search the name first because the data doesn't exist 
yet

void Do(string name)
{
     // get index of name in array
     // can't find x corresponding to name because data is not 
initialized.
     // Can't init data more than once. Can't add init flag(could 
but want to find a better solution)
     Do(x);
}

void Do(int index)
{
     Init_Data();
     ...
}



Now, I could simply make Do a template method but then this 
prevents it being a virtual function.

void Do(T)(T name) if (is(T == string) || is(T == int))
{
     Init_Data();

     static if (is(T == string))
     {
         ...Get index from name
     }

     ....
}



What I really want is a sort of mix between the first overloaded 
method and the second case.


The string version is really just finding the index of the string 
and should insert itself inside the int version similar to the 
static if.

I know there are many ways and many are going to fuss over doing 
it with a bool or duplicate the function or whatever. I'm looking 
for an elegant solution for what I want, I know there are other 
ways... not interested in them. Given that D has so many meta 
capabilities, I'm hoping there is some elegant solution.

To make it clear.

void Do(int index)
{
    // Does stuff

     // If index was a string instead of a name, we would do a 
lookup to find the index for name. Everything else is exactly the 
same

    // does stuff with index
}

void Do(string name)
{
     // somehow
     Do(name_index);
}


Another way is to use a lambda:

void Do(int index, int delegate(data) toName)
{
     // Does stuff
     if (toName) index = toName(data);
     // Do stuff with index
}

void Do(string name)
{
     Do(0, (data) { find i for name; return i; });
     // which plugs in the lambda
}



The problem with all these ways is that they complicate matters 
and either duplicate a lot of code or create hard to maintain 
code or problems in other areas. e.g., if I use the template 
method any literal string is not automatically converted do("this 
won't be treated as a wstring").

If a bool is used, I have to have the initialization code in both 
functions. Doesn't seam like much until you scale the problem up.

What would be nice is something akin to yield:

void Do(int index)
{
     // Does stuff
     ?yield // If Do is called in a special way, we break out of 
the code here
     // Do stuff with index
}

void Do(string name)
{

     yield Do(0);
     find i for name;
     continue Do(i);
}


This keeps everything internal and from the outside everything 
looks as it should, avoids duplicate code, extra arguments, 
flags, etc.

Is it possible?
Jul 11 2016
next sibling parent reply Kagamin <spam here.lot> writes:
Extract functions for shared parts:

void Do(string name)
{
     DoStuff();
     int i = find(name);
     DoStuffWithIndex(i);
}

void Do(int name)
{
     DoStuff();
     DoStuffWithIndex(i);
}
Jul 12 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
On Tuesday, 12 July 2016 at 08:52:26 UTC, Kagamin wrote:
 Extract functions for shared parts:

 void Do(string name)
 {
     DoStuff();
     int i = find(name);
     DoStuffWithIndex(i);
 }

 void Do(int name)
 {
     DoStuff();
     DoStuffWithIndex(i);
 }
I don't like it, creates an extra function for no apparent reason except to get around the problem of not having a yield type of semantic. Again, I wasn't asking for any ol' solution, there are many ways to skin this cat.
Jul 12 2016
next sibling parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
On Tuesday, 12 July 2016 at 13:44:02 UTC, Adam Sansier wrote:
 On Tuesday, 12 July 2016 at 08:52:26 UTC, Kagamin wrote:
 Extract functions for shared parts:

 void Do(string name)
 {
     DoStuff();
     int i = find(name);
     DoStuffWithIndex(i);
 }

 void Do(int name)
 {
     DoStuff();
     DoStuffWithIndex(i);
 }
I don't like it, creates an extra function for no apparent reason except to get around the problem of not having a yield type of semantic. Again, I wasn't asking for any ol' solution, there are many ways to skin this cat.
It is usually considered a good thing to break big functions into smaller ones; this allows for easier code reading, better maintainability and easier reuse. Also note that yield semantics as available in various languages is much different from what you are proposing here.
Jul 12 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
On Tuesday, 12 July 2016 at 13:54:16 UTC, Lodovico Giaretta wrote:
 On Tuesday, 12 July 2016 at 13:44:02 UTC, Adam Sansier wrote:
 On Tuesday, 12 July 2016 at 08:52:26 UTC, Kagamin wrote:
 Extract functions for shared parts:

 void Do(string name)
 {
     DoStuff();
     int i = find(name);
     DoStuffWithIndex(i);
 }

 void Do(int name)
 {
     DoStuff();
     DoStuffWithIndex(i);
 }
I don't like it, creates an extra function for no apparent reason except to get around the problem of not having a yield type of semantic. Again, I wasn't asking for any ol' solution, there are many ways to skin this cat.
It is usually considered a good thing to break big functions into smaller ones; this allows for easier code reading, better maintainability and easier reuse.
Doesn't matter, that isn't what I asked.
 Also note that yield semantics as available in various 
 languages is much different from what you are proposing here.
Not really. Yield is usually a break in flow, regardless just because it's applied to fibers doesn't mean it's *much* different. It's the same basic concept.
Jul 12 2016
parent Lodovico Giaretta <lodovico giaretart.net> writes:
On Tuesday, 12 July 2016 at 16:27:52 UTC, Adam Sansier wrote:
 On Tuesday, 12 July 2016 at 13:54:16 UTC, Lodovico Giaretta
 Also note that yield semantics as available in various 
 languages is much different from what you are proposing here.
Not really. Yield is usually a break in flow, regardless just because it's applied to fibers doesn't mean it's *much* different. It's the same basic concept.
Across various programming languages, yield has two very different meanings and aims: 1) to achieve cooperative multitasking (as in D fibers); but you are not doing cooperative multitasking; 2) for generator functions (like in Python); but you don't have a function that generates multiple values. Also note that in both cases the yielding does not depend on some syntax used at call site. So, by any PL meaning of yield, yield is not what you need to solve your problem.
Jul 12 2016
prev sibling parent reply Kagamin <spam here.lot> writes:
On Tuesday, 12 July 2016 at 13:44:02 UTC, Adam Sansier wrote:
 I don't like it, creates an extra function for no apparent 
 reason except to get around the problem of not having a yield 
 type of semantic. Again, I wasn't asking for any ol' solution, 
 there are many ways to skin this cat.
It's a normal use case for private functions: the public function prepares object state and delegates to the private function that does the logic.
Jul 12 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
On Tuesday, 12 July 2016 at 16:03:15 UTC, Kagamin wrote:
 On Tuesday, 12 July 2016 at 13:44:02 UTC, Adam Sansier wrote:
 I don't like it, creates an extra function for no apparent 
 reason except to get around the problem of not having a yield 
 type of semantic. Again, I wasn't asking for any ol' solution, 
 there are many ways to skin this cat.
It's a normal use case for private functions: the public function prepares object state and delegates to the private function that does the logic.
Doesn't matter, it's not what I asked. Trying to provide answers to a question that wasn't asked and was clearly stated I wasn't interested in those types of answers.
Jul 12 2016
next sibling parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
On Tuesday, 12 July 2016 at 16:30:05 UTC, Adam Sansier wrote:
 Doesn't matter, it's not what I asked. Trying to provide 
 answers to a question that wasn't asked and was clearly stated 
 I wasn't interested in those types of answers.
Every language has its own ways of solving various problems. We are giving you solutions in D. If you don't want solutions in D, then what do you want?
Jul 12 2016
parent Adam Sansier <Adam.Sansier gmail.com> writes:
On Tuesday, 12 July 2016 at 16:42:52 UTC, Lodovico Giaretta wrote:
 On Tuesday, 12 July 2016 at 16:30:05 UTC, Adam Sansier wrote:
 Doesn't matter, it's not what I asked. Trying to provide 
 answers to a question that wasn't asked and was clearly stated 
 I wasn't interested in those types of answers.
Every language has its own ways of solving various problems. We are giving you solutions in D. If you don't want solutions in D, then what do you want?
It's pointless....
Jul 12 2016
prev sibling parent reply Kagamin <spam here.lot> writes:
On Tuesday, 12 July 2016 at 16:30:05 UTC, Adam Sansier wrote:
 Doesn't matter, it's not what I asked.
Yeah, I'm not confident I understood your problem right. You can try to describe your problem better.
Jul 12 2016
parent Adam Sansier <Adam.Sansier gmail.com> writes:
On Tuesday, 12 July 2016 at 17:17:31 UTC, Kagamin wrote:
 On Tuesday, 12 July 2016 at 16:30:05 UTC, Adam Sansier wrote:
 Doesn't matter, it's not what I asked.
Yeah, I'm not confident I understood your problem right. You can try to describe your problem better.
Criteria: 1. At most 2 one parameter functions be used, one that takes an int and the other a wstring. 2. They must be overloadable and allow for a literal string type to be passed for the wstring function. This prevents basic templates and variant techniques. 3. No duplication of code. 4. The wstring function only exists to find the index for the argument passed. It cannot do this without the int function being called first, as it initializes or gets data that cannot be done more than once. This forces some type of "break" in the flow of the int function. suppose you have two functions and only two functions. void Do(int i) { // stuff 1 // stuff 2 (uses i) } void Do(wstring s) { // stuff 1 // converts s to i, the same i that is used in Do(int) // stuff 2 (uses i) } Obviously stuff 1 and stuff 2 are duplicates. stuff 1 does not not use i or s. stuff 2 does not use s. The question is how to optimally, in terms of code duplication, reduce Do(string) so it does no extra work. There are ways, obviously. But to satisfy the complete criteria is the hard part. I could probably use some goto statements and asm to accomplish this, but probably quite a bit of work and tricky void Do(wstring s) { // get i from s (a simple search), no big deal, a comment suffices // create function call to (setup stack, modify first stack parameter, which is i, to use our new i goto Doi; } void Do(int i) { // stuff 1 label Doi; // stuff 2 } The goto bypasses stuff1 in Do(int), and sets up i with the new value. This method would work but is a hack, not portable, etc. It is essentially the same idea as the yield I proposed, but less robust. A yield and continue construct would hide all the details and do something similar. Again, this isn't about how to accomplish some goal, but how to accomplish it given the criteria/restraints proposed. It's a no brainier to do without the constraints.
Jul 12 2016
prev sibling parent reply Meta <jared771 gmail.com> writes:
On Tuesday, 12 July 2016 at 04:23:07 UTC, Adam Sansier wrote:
 Now, I could simply make Do a template method but then this 
 prevents it being a virtual function.

 void Do(T)(T name) if (is(T == string) || is(T == int))
 {
     Init_Data();

     static if (is(T == string))
     {
         ...Get index from name
     }

     ....
 }
You could always use std.variant.algebraic which implements runtime polymorphism: import std.algebraic; alias intOrString = Algebraic!(int, string); void Do(intOrString indexOrName) { Init_Data(); int index = indexOrName.visit!( (int i) => i, (string s) => getIndexByName(s), ); .... }
Jul 12 2016
next sibling parent Meta <jared771 gmail.com> writes:
On Tuesday, 12 July 2016 at 18:52:08 UTC, Meta wrote:
 On Tuesday, 12 July 2016 at 04:23:07 UTC, Adam Sansier wrote:
 Now, I could simply make Do a template method but then this 
 prevents it being a virtual function.

 void Do(T)(T name) if (is(T == string) || is(T == int))
 {
     Init_Data();

     static if (is(T == string))
     {
         ...Get index from name
     }

     ....
 }
You could always use std.variant.algebraic which implements runtime polymorphism: import std.algebraic;
Should be `import std.variant`.
Jul 12 2016
prev sibling parent Adam Sansier <Adam.Sansier gmail.com> writes:
On Tuesday, 12 July 2016 at 18:52:08 UTC, Meta wrote:
 On Tuesday, 12 July 2016 at 04:23:07 UTC, Adam Sansier wrote:
 Now, I could simply make Do a template method but then this 
 prevents it being a virtual function.

 void Do(T)(T name) if (is(T == string) || is(T == int))
 {
     Init_Data();

     static if (is(T == string))
     {
         ...Get index from name
     }

     ....
 }
You could always use std.variant.algebraic which implements runtime polymorphism: import std.algebraic; alias intOrString = Algebraic!(int, string); void Do(intOrString indexOrName) { Init_Data(); int index = indexOrName.visit!( (int i) => i, (string s) => getIndexByName(s), ); .... }
I thought about this, but kinda hacky. Because I'm using wstring I would basically have to do:
 alias intOrString = Algebraic!(int, string, wstring);

 void Do(intOrString indexOrName)
 {
     Init_Data();

     int index = indexOrName.visit!(
         (int i) => i,
         (string s) => getIndexByName(to!wstring(s)),
         (wstring s) => getIndexByName(s),
     );

     ....
 }
It's not terrible, but not better than using templates, which would be more performant. void Do(T arg) if (is(arg == string) || is(arg == int) || is(arg == wstring) { int i = 0; Init_Data(); static if (is(arg == string) || is(arg == wstring)) { wstring s; static if (is(arg == string)) s = to!wstring(arg); else s = arg; // Find i for string i = Findi(arg); } else i = arg; .... } It's not very elegant either but basically works and solves the problem... and also does it in one function. It's probably as close as one can get in D to what I want, I'm hoping someone find a better way.
Jul 12 2016