Delphi 7 with..do statement doesen't work with variant variable - excel

I'm working with Microsoft Excel via Delphi 7. It works fine but while formatting rows and ranges I have to write such long strings.
XLApp.Workbooks[1].WorkSheets[NameDoc].Range['A19:L19'].Font.Bold := true;
So I want to get rid of hard work and do it via "with..do" statement like this
with XLApp.Workbooks[1].WorkSheets[NameDoc] do
begin
Range['A19:L19'].Font.Bold := true;
end;
But at compilation stage I see this error
Record, object or class type required
on string - "with..do".
I creating Excel object this way
XLApp: Variant;
XLApp := CreateOleObject('Excel.Application');
I consider that with..do statement doesen't works with variant type variable, but I want to know whether I'm right or not? And if I'm right is there any workaround to make it work?

Variant can be anything or nothing at all - compiler doesn't know it and cannot know: it is so called "dynamically typed value". Since it does not know - it does not know if there would be any members (properties, methods) and if there would - what names would they have.
To get the benefits of strong compile-time typing - including using of with but not only - you have to use interface variables, those that are provided by TExcelApplication component and underlying unit having those values "statically typerd" - thus providing for Delphi compiler to know value types when compiling, in before running. There are plenty of types like iWorsksheet, iRange and others in that unit.
Borland Delphi 7 TExcelApplication.Connect works on office machines but not at client
http://www.delphipages.com/forum/showthread.php?t=157889
http://delphikingdom.ru/asp/viewitem.asp?catalogid=1270
However, since that is about reference-counting and lifetime I'd suggest you go with explicit use of temp variables rather than using with with and implicit invisible variables. Since you cannot control their lifespan and their clearance you might hit the wall in some unexpected place later. I did.
var tmpR: iRange; // assuming we have statically-typed API
// for example - providing we using ExcelXP or Excel2000 unit
tmpR := XLApp.Workbooks[1].WorkSheets[NameDoc];
tmpR.Range['A19:L19'].Font.Bold := true; // instead of with
with tmpR do // also possible but gives little benefit now
begin // when we made a dedicated temp var
Range['A19:L19'].Font.Bold := true;
end;
tmpR := nil; // crucial unless the most short and simplistic functions
// just release hold on Excel's object - let it manage its memory freely,
// by letting Excel know your program no more uses that object.
Also read
https://en.wikipedia.org/wiki/Automatic_Reference_Counting
https://en.wikipedia.org/wiki/Component_Object_Model

Can with be used with a Variant?
No.
You can use with for types whose members are known at compile time. But variants, for which the . operator is evaluated at run time, do not fall into this category. Hence with is not available for variants.
The documentation says, with my emphasis:
A with statement is a shorthand for referencing the fields of a record
or the fields, properties, and methods of an object. The syntax of a
with statement is:
with obj do statement
or:
with obj1, ..., objn do statement
where obj is an expression yielding a reference to a record, object
instance, class instance, interface or class type (metaclass)
instance, and statement is any simple or structured statement.

Related

Verilog: variable assignment to virtual interface?

Sorry if I had this stupid question...I've been trying to google for answer but couldn't find one. :(
I have a problem assigning a variable to a virtual interface. For example:
Param.sv
...
string MyInput[3];
MyInput[0] = Signal_CLK; //Storing SignalName to in an Array.
MyInput[1] = Signal_Tx;
MyInput[2] = Signal_Rx;
...
MyInterface.sv
...
Signal_CLK = dut.MicroController.Source.clk; //Signal destination
Signal_Tx = dut.MicroController.Tx_01;
Signal_Rx = dut.MicroController.Rx_01;
...
Test.sv
virtual MyInterface my_vif
logic [7:0] read_value;
....
for (i = 0; i <3; i++ )
begin
read_value = my_vif.My_Input[i];
..
//some logic to compare read_value with spec//
..
end
The problem is when compiling, it doesn't translate my_vif.My_Input[0] into my_vif.***dut.MicroController.Source.clk***. Instead, it thinks that the path is my_vif.***My_Input[i]***.
The reason the compiler thinks you are trying to access my_vif.My_Input[i] is because you are. The My_Input[] array is a completely separate string array; not part of the virtual interface. When using the "thing.thing.thing" syntax, the compiler will loyally follow it, so it will expect there to be something called My_Input that has some elements (as its an array) as a member of the interface given by my_vif.
However, looking over youre code, you are trying to have My_Input[i] replaced at compile time, which is very different. The compiler will not run your loop, look in My_Input[i] and find the string "Signal_CLK" and replace that as part of the path to get the path my_vif.Signal_CLK. Nor can it do that at run time.
I dont know of a generic solution to looking over any variables in an interface; though Im also not sure if thats really what you want. IF you provide more details on the rest of your checker, we might be able to help you more.
You cannot use strings to look up identifiers by name within SystemVerilog. There are tool specific and C interfaces that may let you do this, but that would be very inefficient. The best way to do this by using a combination of abstract/concrete classes and the bind construct. See these references: http://events.dvcon.org/2012/proceedings/papers/01P_3.pdf and http://www.doulos.com/knowhow/sysverilog/DVCon08/DVCon08_SysVlog.php

How to trigger COW for string when it doesn't fire automatically

I've got a record, see this question for background info.
TDigits = AnsiString; //Should be `= array of NativeUInt`, but string has COW
TBigint = record
Digit: TDigits; // Unsigned number, LSB stored in D[0], MSB in D[size-1]
Size: Byte; // Mininum = 4, maximum 127.
MSI: Byte; // Most significant (native)integer minimum=1, maximum=127
Sign: Shortint;
class operator Implicit(a: Integer): TBigint;
Background
I'm using a bignum class that (almost) works like normal integers.
So a:= 1000000*10000000*12000000*10000000*1000000; yields perfectly useful results.
For this purpose I use a record with class operators.
These trigger automatic type conversion and initialisation.
Except when there is no conversion, because I'm assigning a TBigint to another TBigint.
The solution
Use an Ansistring to store the core data, it's got copy-on-write and will clone itself when needed.
The problem: (COW does not work if Delphi does not know you're altering the string)
I've got a few pure assembler routines that manipulate the Digit dynamic array disguised as Ansistring.
However when I do something like this:
Label1.Caption:= BigintToStr(b);
..... this fires:
function BigintToStr(const X: TBigint): AnsiString;
var
..
LocX:= x; <<-- assignment, locX and X are joined at the hip.
repeat
D := DivBigint(LocX, 1000000000, LocX); <<-- this routine changes LocX
^^+-- but assembler routines bypass COW
X and LocX are joint at the hip, whatever happens to one happens to the other.
Clearly Delphi does not know that the asm routine DivBigint is changing LocX and therefore a COW is in order.
The workaround
If I change the routine to:
function BigintToStr(const X: TBigint): AnsiString;
var
..
LocX:= x;
LocX.Digit[2]:= #0; <<-- inconsequential change to force COW.
repeat
D := DivBigint(LocX, 1000000000, LocX);
Delphi gets all clued up and performs just fine.
LocX and X are unlinked and everything works fine.
However I don't want to be making silly changes in the middle of some empty space.
Is there a decent/proper/offical* way to force trigger COW in strings?
Something like a system call perhaps?
*circle your favourite option (with a handdrawn circle)
Should be a comment, but need more space...
If you need to call UniqueString or equivalent.
You might as well retain the dynamic record.
A quote from the manual:
Following a call to SetLength, S is guaranteed to reference a unique string or array -- that is, a string or array with a reference count of one. If there is not enough memory available to reallocate the variable, SetLength raises an EOutOfMemory exception.
Note that this behavior even applies when calling SetLength(Length(myArray));.
Delphi will make a copy for you and untangle the problem.
So it turns out there is no need for the complication with the AnsiStrings After all, as long as you call SetLength in every method that accepts the record as a var parameter.
Advantages
This has the added benefit that if you call SetLength to expand the array (as happens often) the added space will be zero-initialized. Something does does not happen with the AnsiString.
Furthermore there is no need to bother with size translations because your array of TXYZ already knows the size of its elements. When using AnsiString you need to add * SizeOf(somestruct) all over the place.
No typecasting is needed, simplifying the code; and in the debugger the data shows up as it is designed.
As you can see the two instances are no longer linked.
Every method that mutates the buffer should, before performing the modification, call UniqueString.
Ensures that a given string has a reference count of one.
In fact, this detail was supplied by Craig Young's comment to my answer to your earlier question.
If you are going to make this viable you are going to need to hide the buffer. Make it strict private. That means that you can only access it from methods of your record. And that way you can be sure that anything that modifies the buffer will call UniqueString.
Personally, I think that a better solution would be to make the type immutable.

Why doesn't TPageProducer remove quotation marks from strings?

I'm trying to debug behaviour that has only appeared when my large app - working fine in XE3 - is run after compiling with XE4. The issue seems to cause some quoted strings (eg "MyString") to retain their quotes even after having been 'de-quoted' by TPageProducer in Web.HTTPProd. For example, consider the code below which is small extract from this Delphi source unit Web.HTTPApp:
procedure ExtractHeaderFields(Separators, _WhiteSpace: TSysCharSet; Content: PChar;
Strings: TStrings; Decode: Boolean; StripQuotes: Boolean = False);
{$ENDIF NEXTGEN}
var
Head, Tail: PChar;
EOS, InQuote, LeadQuote: Boolean;
QuoteChar: Char;
ExtractedField: string;
{$IFNDEF NEXTGEN}
WhiteSpaceWithCRLF: TSysCharSet;
SeparatorsWithCRLF: TSysCharSet;
{$ENDIF !NEXTGEN}
function DoStripQuotes(const S: string): string;
var
I: Integer;
InStripQuote: Boolean;
StripQuoteChar: Char;
begin
Result := S;
InStripQuote := False;
StripQuoteChar := #0;
if StripQuotes then
begin
for I := Result.Length - 1 downto 0 do
if Result.Chars[I].IsInArray(['''', '"']) then
if InStripQuote and (StripQuoteChar = Result.Chars[I]) then
begin
Result.Remove(I, 1);
InStripQuote := False;
end
else if not InStripQuote then
begin
StripQuoteChar := Result.Chars[I];
InStripQuote := True;
Result.Remove(I, 1);
end
end;
end;
I see this called when I use TPageProducer and I can see my good source string go into the ExtractHeaderFields routine above and then into the 'DoStripQuotes' function. Stepping into DoStripQuotes and watching 'Result' shows that it does not change, even when Result.Remove is called (to strip the quote). When I take this 'DoStripQuotes' routine out to a simple test app, it wont compile, telling me that 'Result.anything' is not allowed. I assume then that Result, although it is defined as 'string' must be another type of string in the context of Web.HTTPProd.
So I get to thinking maybe this is something to do with the 'Immutable strings' that I've heard about. I read this SO question about that and although I get the gist, I could do with more practical advice.
Specifically, I would like answers to the following questions:
What type of 'string' is 'Result' if the notation Result.Length is allowed?
Is there a way in which I can tell the compiler to use 'XE3' compatibility for a unit? (THis might allow me to see where the problem is originiating). Ive ttried {$ZEROBASEDSTRINGS ON} / OFF but this seems to cause even more chaos and I don't know what I'm doing!
Thanks for any help.
LATER EDIT: As noted in the accepted answer below this is a bug in the VCL unit Web.HTTPApp.pas which should read "Result := Result.Remove(I,1)" in two places around line 2645 and not "Result.Remove(I,1)"
What type of 'string' is 'Result' if the notation Result.Length is allowed?
It's just the same old string, aliased to UnicodeString, that you've been using since Delphi 2009. The difference is that this code uses the new record helper (specifically SysUtils.TStringHelper). That's what lets you use . notation on a string variable.
Is there a way in which I can tell the compiler to use 'XE3' compatibility for a unit?
No. The code in question is a library unit and it is designed to be compiled in a particular mode. What's more, you can't readily re-compile it unless you take on compiling the RTL/VCL yourself. Even if there was such a mode, it would not help since the code is simply wrong (see below). No amount of mode switching can fix this particular piece of code.
I get to thinking maybe this is something to do with the Immutable strings that I've heard about.
It's not. None of the Delphi compilers have immutable strings yet. The concept of immutable strings is just something that has been floated as a future change. And if the change is made, expect it to be made in the mobile compilers first.
The problem is in fact just a rather simple bug in the code that you posted which has clearly had no testing at all. The use of Remove is wrong. That method does not modify the string in-place. Instead it returns a new string that has the character removed. The code should read:
Result := Result.Remove(I, 1);
The reason that the developer who coded ExtractHeaderFields has made this mistake is that whoever designed the string helper code named the Remove method incorrectly. Since Remove is a verb you would expect it to operate in-place. A method that does not modify the subject, and returns a new instance, as this method does, should be given a name that is a noun. So this method should be named something like Remnants. It looks to me as though the RTL designers copied the .net naming where the same flaw also exists.
You should submit a QC report, if one does not already exist. I know that XE4 update 1 has just been released. It's plausible that it contains a fix.
Your other options, as I see them, are:
Stick with XE3 until XE4 is sufficiently debugged.
Include a copy of the Web.HTTPApp unit in your project and fix the bugs yourself.

Why assign a reference to a struct in go?

I'm having a look at the code at this page:
http://golang.org/pkg/net/http/
And there's one thing I don't understand - at some point, a new structure is created and initialized like this:
client := &http.Client{
CheckRedirect: redirectPolicyFunc,
}
Why use & when creating this structure?
I've also read this blog post and structs are initialized like this:
r := Rectangle{}
What is the difference between both and how should I know which one to use?
The difference is in the type of your variable.
client := &http.Client{
makes client of type *http.Client
while
client := http.Client{
builds a http.Client.
The top one is returning a pointer. It is a Go idiom instead of using new. The second one is just a value object. If you need a pointer use the top.
Check the effective go doc for more about this
http://golang.org/doc/effective_go.html#allocation_new
In object-oriented programming, in order for an object to have dynamic lifetime (i.e. not tied to the current function call), it needs to be dynamically allocated in a place other than the current stack frame, thus you manipulate the object through a pointer. This is such a common pattern that in many object-oriented languages, including Java, Python, Ruby, Objective-C, Smalltalk, JavaScript, and others, you can only deal with pointers to objects, never with an "object as a value" itself. (Some languages though, like C++, do allow you to have "objects as values"; it comes with the RAII idiom which adds some complexity.)
Go is not an object-oriented language, but its ability to define custom types and define methods that operates on that custom type, can be made to work very much like classes and methods. Returning a pointer to the type from the "constructor" function allows the "object" to have a dynamic lifetime.
When we use reference, we use a single item throughout the program runtime. Even if we assign that to a new variable or pass through a function. But when we use value, we make new copies of individual items.
( Reference is not right word according to golang convention. "Address of value" would be more appropriate here https://golang.org/ref/spec#Package_initialization )
An example will make it much clear I hope.
type Employee struct {
ID int
Name string
Address string
}
func main() {
andy := &Employee{}
andy.Name = "Andy"
brad := andy
brad.Name = "Brad"
fmt.Println(andy.Name)
}
The result of this code block would be:
Brad
As we made new variable from it but still referring to same data. But if we use value instead of reference and keep the rest of the code same.
// from
andy := &Employee{}
// to
andy := Employee{}
This time the result would be:
Andy
As this time they both are individual items and not referring to same data anymore.

Is it possible to take the name of a variable and turn it into a string in ActionScript 3.0?

I am making a simple debugger window in ActionScript for myself where I can add and remove variables I want to track. I was to be able to add variables to the list by just doing something like
DebuggerMonitor.trackVar(variable).
My question is, is there any way I can turn "variable" itself (the name, not the value) into a String to be added into a text field?
Depending on how "intelligent" your debugger should be, you could just pass the name along:
DebuggerMonitor.trackVar( variable, "variable" );
since obviously, when used in a context like this, the name should be known at the time you are writing the program.
You can also do some reflection magic to get instance variable names, but it won't work for temp variables (their names are dropped at compilation time):
public function getVariableName( instance:*, match:* ):String {
var typeDescription:XML = describeType( instance );
var variables:XMLList = typeDescription..variable;
var accessors:XMLList = typeDescription..accessor;
for each(var variable:XML in variables)
if(matchesXMLName( instance, variable, match ))
return variable.#name;
for each(var accessor:XML in accessors)
if(matchesXMLName( instance, accessor, match ))
return accessor.#name;
return "No name found.";
}
private function matchesXMLName( instance:*, xml:XML, match:* ):Boolean {
return match == instance[xml.#name.toString()];
}
var varName:String = getVariableName ( myObject, variable );
Using reflections like this will also be quite costly, if used often - you will have to think of a way to cache the type descriptions.
I recommend you check out the as3commons reflections package - there is a lot of useful functionality in there...
Short answer - No :(
You can access the type name but not individual instance names, as these are lost at run-time.
There is a confusion caused by the keyword 'var' because it is used to create several types of bindings.
Lexical bindings (the keyword 'var' was used inside a function).
Dynamic bindings (the keyword 'var' was used to declare a class' field).
Lexical bindings are interpreted by the compiler at compile time as addresses of the registers of the registers space occupied by the function. The names given to lexical bindings perish at this time and it is not possible to restore them at runtime - therefore you can't get the "name" of the variable.
Dynamic bindings are a kind of "public API" of the objects that declare them, they may be accessed from the code that was not compiled together with the code that created them, this is why, for the purpose of reflection the names of these bindings are stored in compiled code. However, ActionScript has no way of referencing LHS values, so you cannot, even if you know the name of the variable and the object declaring it, pass it to another function. But you can look it up in the debugger or by calling describeType on the object declaring the variable. Note that describeType will not show information on private variables even if you are calling it from the scope of the object in question.

Resources