Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 5 years ago.
Improve this question
It happens to me quite often that I call a function Foo and want to know what exceptions this function might throw. In order to find out I then look into the implementation of Foo, but that is not enough. Foo might indeed call a function Bar that raises an exception.
Sometimes I even miss Java's checked exception handling.
So it is obivous to me that it is necessary to document the exceptions each function can throw: the question is: how? Are there any best practices on how to document exceptions? How do you handle this problem?
I think this covers some part of the problem you became aware of
Cleaner, more elegant and wrong
Cleaner, more elegant and harder to recognize
Most Delphi applications are VCL applications. They do not require a checked exception, because the main message loop has a try/except block catching everything.
It can be good practice to document which exceptions can be explicitly raised by your code though.
I'd use XMLDoc for that (there are various questions on XMLDoc her on SO, and here is some documentation from Embarcadero).
Note however that underlying code can also raise exceptions. Depending on the influence you have on libraries, you can or cannot assure those are always the same. A different thing is the OS: depending on where you run, you can get different exceptions.
--jeroen
We use Javadoc style comments for documentation. We extract the info and generate the output with some simple text scripts. We have used DelphiCodeToDoc, too.
Documenting exceptions, we have mandated to use the #throws tag.
this is looking great for documenting code - Documentation Insight from DevJet.net
I use XMLDoc comments. It's basically adding a specialized type of comment to your code in the interface section, just above the property or method declarations. Here's a nonsensical (of course) example. If you add similar style comments in your code, they'll pop up in Code Insight when you invoke it while writing code, just like the VCL's documentation does.
type
{$REGION 'TMyClass description'}
/// <summary>TMyClass is a descendent of TComponent
/// which performs some function.</summary>
{$ENDREGION}
TMyClass=class(TComponent)
private
// your private stuff
FSomeProp: Boolean;
procedure SetSomeProp(Value: Boolean);
protected
// your protected stuff
public
{$REGION 'TMyClass constructor'}
/// <summary> TMyClass constructor.</summary>
/// <remarks>Creates an instance of TMyClass.</remarks>
/// <param>Owner: TObject. The owner of the instance of TMyClass</param>
/// <exception>Raises EMyObjectFailedAlloc if the constructor dies
/// </exception>
{$ENDREGION}
constructor Create(Owner: TObject); override;
published
{$REGION 'TMyClass.Someprop'}
/// <summary>Someprop property</summary>
/// <remarks>Someprop is a Boolean property. When True, the
/// thingamajig automatically frobs the widget. Changing this
/// property also affects the behavior of SomeOtherProp.</remarks>
{$ENDREGION}
property Someprop: Boolean read FSomeProp write SetSomeProp;
end;
I prefer to wrap these XMLDoc comments in regions, so they can be collapsed out of the way unless I want to edit them. I've done so above; if you don't like them, remove the lines with {$REGION } and {$ENDREGION}
I use PasDoc for documenting almost all of my Delphi projects. It includes a "raises" tag which does what you seem to be asking for.
Regards
- turino
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I know dynamic objects are objects created during run time. I have to make use of dynamic objects for a project, but not quite sure what to do. I'm not too sure what dynamic objects actually are except for the definition and for example once a button is clicked a panel is created, but this isn't exactly what is wanted from me. It is supposed to be more complex and meaningful.
What are other examples of dynamic objects and when or why would you use it?
Any instance of a class created at runtime in your code, and freed when you no longer need them. This happens all the time for classes that aren't visual components and can't just be dropped on a form.
Basic examples are things like TStringList for working with strings or text files, TStream descendants like TFileStream or TResourceStream, threads using TThread, generic lists such as TList<T>, and many other classes.
For instance, to work with a text file, an example of a dynamically created TStringList could be as follows:
var
SL: TStringList;
begin
SL := TStringList.Create;
try
SL.LoadFromFile('C:\Temp\MyFile.txt');
SL[0] := 'I changed this line.';
SL.SaveToFile('C:\Temp\MyFile.txt');
finally
SL.Free;
end;
end;
This sounds like a class project, and you don't seem to understand some of the terminology involved.
Just to generalize on this a little bit, in Delphi, instances of ANY class you define need to be created dynamically. This contrasts with records, which can be declared statically as variables as well as allocated dynamically.
In both cases (classes and records), when you create an instance dynamically, the memory for them is allocated from the heap, rather than the stack (where local vars are created). But the syntax is different in each case.
In Delphi, this contrasts sharply with C++, because C++ classes are basically structs (like Delphi records) with some embellishments, meaning that you can declare instances of classes in C++ as member variables where their memory is allocated on the stack in the same way you can declare instances of structs, as well as records in Pascal.
But this doesn't work in Delphi, even though the syntax looks the same. That's because in Delphi, the variables referring to classes are really pointers, like in C++, but they're automatically dereferenced, so they look syntactically as if they're just instances rather than a pointer-to-an-instance.
In Delphi, if you're working with a class, you MUST create it dynamically (by calling TMyObj.Create(...)). Thus, every class has a constructor associated with it; if you don't define one, the default TObject.Create constructor is called. (This is again different than in C++ where a default constructor will be created for you by the compiler for any class that needs one and it's not defined.)
I'll repeat this again so you're clear about it. Anything defined like THIS in Delphi:
type
Twhatever = class
is equivalent to this:
type
Twhatever = class( TObject )
and absolutely MUST be created dynamically in your code like this:
var
xyz : Twhatever; // xyz is really a pointer here! It's NOT an "instance"
begin
xyz := Twhatever.Create(); // you need to create an instance using the Create() method
try
// do some stuff
finally
xyz.Free;
end;
You cannot NOT use xyz without first creating an instance of it via it's .Create method.
I have discovered (the hard way) that if a file has a valid UTF-8 BOM but contains any invalid UTF8 encodings, and is read by any of the Delphi (2009+) encoding-enabled methods such as LoadFromFile, then the result is a completely empty file with no error indication. In several of my applications, I would prefer to simply lose a few bad encodings, even if I get no error report in this case either.
Debugging reveals that MultiByteToWideChar is called twice, first to get the output buffer size, then to do the conversion. But TEncoding.UTF8 contains a private FMBToWCharFlags value for these calls, and this is initialized with a MB_ERR_INVALID_CHARS value. So the call to get the charcount returns 0 and the loaded file is completely empty. Calling this API without the flag would 'silently drop illegal code points'.
My question is how best to weave through the nest of classes in the Encoding area to work around the fact that this is a private value (and needs to be, because it is a class var for all threads). I think I could add a custom UTF8 encoding, using the guidance in Marco Cantu's Delphi 2009 book. And it could optionally raise an exception if MultiByteToWideChar has returned an encoding error, after calling it again without the flag. But that does not solve the problem of how to get my custom encoding used instead of Tencoding.UTF8.
If I could just set this up as a default for the application at initialization, perhaps by actually modifying the class var for Tencoding.UFT8, this would probably be sufficient.
Of course, I need a solution without waiting to lodge a QC report asking for a more robust design, getting it accepted, and seeing it changed.
Any ideas would be very welcome. And can someone confirm this is still an issue for XE4, which I have not yet installed?
I ran into the MB_ERR_INVALID_CHARS issue when I first updated Indy to support TEncoding, and ended up implementing a custom TEncoding-derived class for UTF-8 handling to avoid specifying MB_ERR_INVALID_CHARS. I didn't think to use a class helper.
However, this issue is not just limited to UTF-8. Any decoding failure of any of the TEncoding classes will result in a blank result, not an exception being raised. Why Embarcadero chose that route, when most of the RTL/VCL uses exceptions instead, is beyond me. Not raising an exception on error caused a fair amount of issues in Indy that had to be worked around.
This can be done pretty simply, at least in Delphi XE5 (have not checked earlier versions). Just instantiate your own TUTF8Encoding:
procedure LoadInvalidUTF8File(const Filename: string);
var
FEncoding: TUTF8Encoding;
begin
FEncoding := TUTF8Encoding.Create(CP_UTF8, 0, 0);
// Instead of CP_UTF8, MB_ERR_INVALID_CHARS, 0
try
with TStringList.Create do
try
LoadFromFile(Filename, FEncoding);
// ...
finally
Free;
end;
finally
FEncoding.Free;
end;
end;
The only issue here is that the IsSingleByte property for the newly instantiated TUTF8Encoding is then incorrectly set to False, but this property is not currently used anywhere in the Delphi sources.
A partial workaround is to force the UTF8 encoding to suppress MB_ERR_INVALID_CHARS globally. For me, this avoids the need for raising an exception, because I find it makes MultiByteToWideChar not quite 'silent': it actually inserts $fffd characters (Unicode 'replacement character') which I can then find in the cases where this is important. The following code does this:
unit fixutf8;
interface
uses System.Sysutils;
type
TUTF8fixer = class helper for Tmbcsencoding
public
procedure setflag0;
end;
implementation
procedure TUTF8fixer.setflag0;
{$if CompilerVersion = 31}
asm
XOR ECX,ECX
MOV Self.FMBToWCharFlags,ECX
end;
{$else}
begin
Self.FMBToWCharFlags := 0;
end;
{$endif}
procedure initencoding;
begin
(Tencoding.UTF8 as TmbcsEncoding).setflag0;
end;
initialization
initencoding;
end.
A more useful and principled fix would require changing the calls to MultiByteToWideChar not to use MB_ERR_INVALID_CHARS, and to make an initial call with this flag so that an exception could be raised after the load is complete, to indicate that characters will have been replaced.
There are relevant QC reports on this issue, including 76571, 79042 and 111980. The first one has been resolved 'as designed'.
(Edited to work with Delphi Berlin)
Your "global" approach is not really global - it relies upon the assumption that all the code would only use one and the same instance of TUTF8Encoding. The same instance where you hacked the flags field.
But it would not work if one obtain TUTF8Encoding object(s) by other means than TEncoding.GetUTF8, for example in XE2 another method - TEncoding.GetEncoding(CP_UTF8) - would create a new instance of TUTF8Encoding instead of re-using FUTF8 shared one. Or some function might run TUTF8Encode.Create directly.
So i'd suggest two more approaches.
Approach with patching the class implementation, somewhat hacky. You introduce your own class for the sake of obtaining new "fixes" constructor body.
type TMyUTF8Encoding = class(TUTF8Encoding)
public constructor Create; override;
end;
This constructor would be the copycat of TUTF8Encoding.Create() implementation, except for setting the flag as you want it ( in XE2 it is done by calling another, inherited Create(x,y,z) so u would not need an access to the private field ) instead.
Then you can patch the stock TUTF8Encoding VMT overriding its virtual constructor to that new constructor of yours.
You may read Delphi documentation about "internal formats" and so forth, to get the VMT layout. You would also need calling VirtualProtect (or other platform-specific function) to remove protection from VMT memory area before patching and then to restore it.
Examples to learn from
How to change the implementation (detour) of an externally declared function
https://stackoverflow.com/a/1482802/976391
Or you may try using Delphi Detours library, hopefully it can patch virtual constructors. Then... it might be an overkill here to use that rather complex lib for that single goal.
After you hacked the TUTF8Encoding class do call the TEncoding.FreeEncodings to remove the already created shared instances (if any) if any and thus trigger recreating the UTF8 instances with your modifications.
Then, if you compile your program as a single monolithic EXE , without using runtime BPL modules, you just can copy the SysUtils.pas sources to your application folder and then to include that local copy into your project explicitly.
How to patch a method in Classes.pas
There you would change the very TUTF8Encoding implementation as you see fit in the sources and Delphi would use it.
This brain-deadly simplistic (hence - equally reliable) approach would not work though if your projects would be built to reuse rtlNNN.bpl runtime package instead of being monolithic.
I'm asking this because I'm out of good ideas...hoping for someone else's fresh perspective.
I have a user running our 32-bit Delphi application (compiled with BDS 2006) on a Windows 7 64-bit system. Our software was "working fine" until a couple weeks ago. Now suddenly it isn't: it throws an Access Violation while initializing (instancing objects).
We've had him reinstall all our software--starting all over from scratch. Same AV error. We disabled his anti-virus software; same error.
Our stack tracing code (madExcept) for some reason wasn't able to provide a stack trace to the line of the error, so we've sent a couple error logging versions for the user to install and run, to isolate the line which generates the error...
Turns out, it's a line which instances a simple TStringList descendant (there's no overridden Create constructor, etc.--basically the Create is just instancing a TStringList which has a few custom methods associated with the descendant class.)
I'm tempted to send the user yet another test .EXE; one which just instances a plain-vanilla TStringList, to see what happens. But at this point I feel like I'm flailing at windmills, and risk wearing out the user's patience if I send too many more "things to try".
Any fresh ideas on a better approach to debugging this user's problem? (I don't like bailing out on a user's problems...those tend to be the ones which, if ignored, suddenly become an epidemic that 5 other users suddenly "find".)
EDIT, as Lasse requested:
procedure T_fmMain.AfterConstruction;
begin
inherited;
//Logging shows that we return from the Inherited call above,
//then AV in the following line...
FActionList := TAActionList.Create;
...other code here...
end;
And here's the definition of the object being created...
type
TAActionList = class(TStringList)
private
FShadowList: TStringList; //UPPERCASE shadow list
FIsDataLoaded : boolean;
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
procedure DataLoaded;
function Add(const S: string): Integer; override;
procedure Delete(Index : integer); override;
function IndexOf(const S : string) : Integer; override;
end;
implementation
procedure TAActionList.AfterConstruction;
begin
Sorted := False; //until we're done loading
FShadowList := TStringList.Create;
end;
I hate these kind of problems, but I reckon you should focus on what's happening recently BEFORE the object tries to get constructed.
The symptoms you describe sound like typical heap corruption, so maybe you have something like...
An array being written to outside bounds? (turn bounds checking on, if you have it off)
Code trying to access an object which has been deleted?
Since my answer above, you've posted code snippets. This does raise a couple of possible issues that I can see.
a: AfterConstruction vs. modified constructor:
As others have mentioned, using AfterConstruction in this way is at best not idiomatic. I don't think it's truly "wrong", but it's a possible smell. There's a good intro to these methods on Dr. Bob's site here.
b: overridden methods Add, Delete, IndexOf
I'm guessing these methods use the FshadowList item in some way. Is it remotely possible that these methods are being invoked (and thus using FShadowList) before the FShadowList is created? This seems possible because you're using the AfterConstruction methods above, by which time virtual methods should 'work'. Hopefully this is easy to check with a debugger by setting some breakpoints and seeing the order they get hit in.
You should never override AfterConstruction and BeforeDestruction methods in your programs. They are not meant for what you're doing with them, but for low-level VCL hacking (like reference adding, custom memory handling or such).
You should override the Create constructor and Destroy destructor instead and put your initialization code here, like such:
constructor TAActionList.Create;
begin
inherited;
// Sorted := False; // not necessary IMHO
FShadowList := TStringList.Create;
end;
Take a look at the VCL code, and all serious published Delphi code, and you'll see that AfterConstruction and BeforeDestruction methods are never used. I guess this is the root cause of your problem, and your code must be modified in consequence. It could be even worse in future version of Delphi.
Clearly there is nothing suspicious about what TAActionList is doing at time of construction. Even considering ancestor constructors and possible side-effects of setting Sorted := False indicate there shouldn't be a problem. I'm more interested in what's happening inside T_fmMain.
Basically something is happening that causes FActionList := TAActionList.Create; to fail, even though there is nothing wrong in the implementation of TAActionList.Create (a possibility is that the form may have been unexpectedly destroyed).
I suggest you try changing T_fmMain.AfterConstruction as follows:
procedure T_fmMain.AfterConstruction;
begin
//This is safe because the object created has no form dependencies
//that might otherwise need to be initialised first.
FActionList := TAActionList.Create;
//Now, if the ancestor's AfterConstruction is causing the problem,
//the above line will work fine, and...
inherited AfterConstruction;
//... your error will have shifted to one of these lines here.
//other code here
end;
If an environment issue with a component used by your form is causing it destroy the form during AfterConstruction, then it's the assignment of the new TAActionList.Create instance to FActionList that's actually causing the AV. Another way to test would be to first create the object to a local variable, then assign it to the class field: FActionList := LActionList.
Environment problems can be subtle. E.g. We use a reporting component which we discovered requires that a printer driver is installed, otherwise it prevents our application from starting up.
You can confirm the destruction theory by setting a global variable in the form's destructor. Also you may be able to output a stack trace from the destructor to confirm the exact sequence leading to the destruction of the form.
Our software was "working fine" until a couple weeks ago... suddenly become an epidemic that 5 other users suddenly "find".) :
Sounds like you need to do some forensic analysis, not debugging: You need to discover what changed in that user's environment to trigger the error. All the more so if you have other users with the same deployment that don't have the problem (sounds like that's your situation). Sending a user 'things to try' is one of the best ways to erode user confidence very quickly! (If there is IT support at the user site, get them involved, not the user).
For starters, explore these options:
*) If possible, I'd check the Windows Event Log for events that may have occurred on that machine around the time the problem arose.
*) Is there some kind of IT support person on the user's side that you can talk to about possible changes/problems in that user's environment?
*) Was there some kind of support issue/incident with that user around the time the error surfaced that may be connected to it, and/or caused some kind of data or file corruption particular to them?
(As for the code itself, I agree with #Warran P about decoupling etc)
Things to do when MadExcept is NOT Enough (which is rare, I must say):
Try Jedi JCL's JCLDEBUG instead. You might get a stack traceback with it, if you change out MadExcept for JCLDEBUG, and write directly the stack trace to the disk without ANY UI interaction.
Run a debug-viewer like MS/SysInternals debugview, and trace output things like the Self pointers of the objects where the problems are happening. I suspect that somehow an INVALID instance pointer is ending up in there.
Decouple things and refactor things, and write unit tests, until you find the really ugly thing that's trashing you. (Someone suggested heap corruption. I often find heap corruption goes hand in hand with unsafe ugly untested code, and deeply bound UI+model cascading failures.)
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
What Design Patterns do you implement in common Delphi programming? What patterns are easier to adapt in Delphi programming? (Every language is excellent in different fields, so what patterns are likely to be very strong structures when using Delphi?)
I would be glad, if you could tell about some changes in design patterns for Delphi 2009 / 2010 (since those support generics, and RTTI in 2010).
There are many articles out there in the wild Internet, but they doesn't discuss the everyday usability and changes in patterns. (Most of them just discuss changes in language specifics, architecture).
Only a minority of the Delphi developers knows that every Delphi developer uses a Factory pattern (delphi.about.com has an example in "regular" Delphi), but then implemented using virtual Create constructors.
So: time to shed some light on that :-)
Virtual constructors are to classes like virtual methods are like object instances.
The whole idea of the factory pattern is that you decouple the logic that determines what kind (in this case "class") of thing (in this case "object instance") to create from the actual creation.
It works like this using virtual Create constructors:
TComponent has a virtual Create constructor so, which can be overridden by any descending class:
type
TComponent = class(TPersistent, ...)
constructor Create(AOwner: TComponent); virtual;
...
end;
For instance the TDirectoryListBox.Create constructor overrides it:
type
TDirectoryListBox = class(...)
constructor Create(AOwner: TComponent); override;
...
end;
You can store a class reference (the class analogy to an object instance reference) in a variable of type 'class type'. For component classes, there is a predefined type TComponentClass in the Classes unit:
type
TComponentClass = class of TComponent;
When you have a variable (or parameter) of type TComponentClass, you can do polymorphic construction, which is very very similar to the factory pattern:
var
ClassToCreate: TComponentClass;
...
procedure SomeMethodInSomeUnit;
begin
ClassToCreate := TButton;
end;
...
procedure AnotherMethodInAnotherUnit;
var
CreatedComponent: TComponent;
begin
CreatedComponent := ClassToCreate.Create(Application);
...
end;
The Delphi RTL uses this for instance here:
Result := TComponentClass(FindClass(ReadStr)).Create(nil);
and here:
// create another instance of this kind of grid
SubGrid := TCustomDBGrid(TComponentClass(Self.ClassType).Create(Self));
The first use in the Delphi RTL is how the whole creation process works of forms, datamodules, frames and components that are being read from a DFM file.
The form (datamodule/frame/...) classes actually have a (published) list of components that are on the form (datamodule/frame/...). That list includes for each component the instance name and the class reference.
When reading the DFM files, the Delphi RTL then:
finds about the components instance name,
uses that name to find the underlying class reference,
then uses the class reference to dynamically create the correct object
A regular Delphi developer usually never sees that happen, but without it, the whole Delphi RAD experience would not exist.
Allen Bauer (the Chief Scientist at Embarcadero), wrote a short blog article about this topic as well.
There is also a SO question about where virtual constructors are being used.
Let me know if that was enough light on the virtual Create constructor topic :-)
--jeroen
You can find an excellent article by Marco Cantu on the equivalence of GOF patterns and Delphi idioms. I remember attending his Borcon session on the subject, it was excellent.
One main idea to remember is that design patterns are needed to supplement shortcomings of the language/framework. And if you have a native idiom, you don't need to reinvent the wheel and implement the whole GOF shebang, just learn to recognize it and name it (as Jeroen did with his superb explanation on the Factory).
I use frequently following patterns:
Command
Visitor
Table Data Gateway
Observer
Adapter
Singleton (with many care!)
Abstract Factory
Factory Method
State
Dependency Injection in all of his form
Facade
Service Locator
Separated Interface
I frequently uses the following patterns:
Observer in MVC
Singlton
Template Method
State
Non-OOP programming (some call it Structured programming) is very common with Delphi programmers. It is very simple: You create a function that does something, and it is not related to a record/object-like data structure. Example: IntToStr()
Delphi does this very well, because encapsulation is delivered using interface/implementation sections, and because the resulting machine code is extremely efficient. When compiling, it also supports optimizations for that, for instance, if you have a typed constant in your interface section, and the program is fully compiled - if you then change the value of that constant, the unit is not recompiled, only the constant changes. This is not really necessary in a daily work, but it is an example of how Delphi works.
An ordinary Unit behaves like a singleton. You can't use OOP-techniques like inheritance and polymorfism though, but that might be a good thing :)
I generally think that Delphi makes it too easy to avoid sound oop design. That is nice for RAD, but you need to know which pitfalls to avoid if you want a flixible and maintainable code. Eg the public visibility for the components you add to the forms, the global Form1 variable of type TForm1 (instead of manually managed lifetime and a base class as type) and the lack of seperation between GUI and business logic. Just to mention some issues.
In a class declaration, you can press Ctrl+Space to get a list of virtual methods in the baseclass that you can override.
This list seems to be very limited, though. Ex.
TMyBaseClass = class(TInterfacedObject)
protected
procedure mymethod; virtual;
end;
TMyClass = class(TMyBaseClass)
protected
{Ctrl+Space here...}
end;
In TMyClass, I get methods from TInterfacedObject and TObject, but not from TMyBaseClass. Why is that?
-Vegar
EDIT: Forgot my delphi version... I'm using 2007.
Because sometimes these IDE tools work and sometimes they don't (and afaik this is not a problem that is unique to Delphi/RAD Studio).
For example, if you have other edits outstanding which mean that the IDE cannot adequately parse your source up to the current insertion point, then it is likely that such things will be "broken".
In such cases I personally try a quick Ctrl+F9 to see if there are compilation errors that I perhaps may not be aware of. 9 times out of 10 there are, and fixing those then fixes the code insight behaviour.
Recreating your example verbatim in a new unit, I get "mymethod" in my code completion list as expected, so is it possible that in your actual case that your base class and your derived class are actually separated by other code, perhaps even in separate units, and that that other code contains errors "between" the two declarations at the time at which you are trying to invoke code completion?
As a more general point, ime it's better to treat such things as "nice to haves when they work" but to try to develop habits and practices that don't rely on them. In this case the only substitute is knowledge of the methods to be overridden, which isn't much help I admit.