-
Notifications
You must be signed in to change notification settings - Fork 20
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Move IFDEF's to specialized units #42
Comments
@nunopicado what do you think? |
Unfortunately, with IFDEF's, we tend to need them in several moments inside a single unit, sometimes even inside a single method we need to use them more than once. I agree conditional directive affect negatively the readability of the code, but I'm not quite sure those inc files would be a solution, rather a bigger problem in the maintainability of the code. I once had the same problem, and my solution was to have a proxy unit (meaning only one additional file per unit) where the conditional directives would be centralized as functions the main unit would consume without conditional directives. I'm not sure this approach would fit this project concept, though... |
Sorry but I didn't understand why that. |
hmm maybe it was I who didn't understand your idea at first. That would indeed only require 3 files, but wouldn't that also mean duplicated code (for all the code which has the same implementation in both FPC and Delphi) for every unit that requires conditional compilation no matter how small the change is? |
Yes and no. It will depend of the implementation, for example, if a classe has so many methods the code will be duplicated to each plataform. But then this code is "wrong", we should decompose it in little parts to minimaze the duplication — only the signature will be duplicate, not the implementation in these cases. Here an example. Look this unit here and tell me what do you think about this idea. |
I actually don't like that unit very much. I can't tell why, but I think clauses should be grouped by kind, and the idea of having multiple const clauses throughout the same section of the unit feels a little strange. That being said, I don't think it is a problem with the number of methods. Now, imagine the following. You have a class with 3 methods and 2 constructors. Still well within reasonable limits, right? What would we do then? Of course we could put the other methods bellow the {$include} directive, so they wouldn't be in the inc file, thus would not be duplicated. But that feels like spaghetti code. Having a class implemented partially on a file and the rest on another feels itchy. Besides, if the uses clause sits inside the inc file, we'll have another problem, also with duplicated code. |
You're right... but I didn't think to code this way. uses
Classes, SysUtils,
{$IFDEF FPC}
md5,
{$ELSE}
hash,
{$ENDIF} Then, we have more conditionals: function TMD5Stream.GetStream: IDataStream;
begin
Result := TDataStream.New(
{$IFDEF FPC}
MD5Print(
MD5String(
FOrigin.AsString
)
)
{$ELSE}
THashMD5.GetHashString(
FOrigin.AsString
)
{$ENDIF}
);
end; At a first place, should not exist functions to calculate MD5 inside this object. But this algorithm or better, this MD5 object, has different implementations depending at the compiler. What I'm proposing is that:
So, in the main {$IFDEF fpc}
{$include james.crypto.md5.fpc.inc}
{$ELSE}
{$include james.crypto.md5.delphi.inc}
{$ENDIF} So we can use What do you think? [1] I improved the namespaces |
The use of ".inc" instead ".pas" is just to users don't use these files directly... but maybe is better write a real unit and allow users make their choices. My main point here is: If you have different implementation to the same concept, we should create others classes, instead of using IFDEF's in just one class. |
So in the main unit (james.crypto.md5) we would call the object THashMD5, that would sit inside the inc file. Looks better then, but... What if THashMD5, being an object on it's own, would sit not on an inc file, but rather on a full blown unit? Say, james.crypto.md5.delphi.pas and james.crypto.md5.fpc.pas. james.crypto.md5 would have only one compiler directive, in its uses clause, and nothing else. It would call the required functions from the respective unit, when needed. Actually, this is more or less what I was referring to when I told you up in this thread about the proxy functions. |
Hadn't read your last comment... |
Ok. Let's try this way. |
@nunopicado please, take a look at these commits above. Try to run into Delphi and tell me what do you think about. |
Yes, I think is a much better option than the previous inc file based solution. type
TMD5Hash =
{$IFDEF fpc}
James.Crypto.MD5.FPC.TMD5Hash;
{$ELSE}
James.Crypto.MD5.Delphi.TMD5Hash;
{$ENDIF} This isn't required for a successful compilation. Since the idea was avoid proliferation of compiler directives, could you please explain the choice to include this? Is this for readability or some other reason I'm not quite following? |
Why not? The |
Not necessary, I don't have anything against the alias. |
Just to organize and make the developer's life easier — me too. If this class will be only used in So, the goal when I created this alias is to maintain all MD5 classes together in one place for developers do not need to think about compilers and IFDEF's. Do you see now? |
So it was about readability... 👍 |
Not only about readability, but about encapsulation: We are encapsulating the cross compiler problem into the classes. |
But they wouldn't even without the alias. |
Exactly that. |
I did. I didn't get the idea before, but it's all clear now. |
In this unit we have the code below:
Let's move all
$IFDEF
from units that is part of API to another specialized units, ie, each unit that can be used by final developers should not have$IFDEF
.I'm thinking of something like that:
So, each implementation could be isolated.
The text was updated successfully, but these errors were encountered: