I want to output a header line in a log file and then a line of '-' before the data. To do this I create a string of the header and then outpout the same number of '-'.
But the below code always fails with a CONSTRAINT_ERROR because the generated string is not 1024 characters. In Ada string assignments r开发者_如何学编程equire exactly the same length not just sufficient capacity.
Option 1) is to compute the exact length but that is brittle to future changes. Option 2) is to use something other than String.
procedure F() is
Msg : String(1..1024);
begin
Open_Log();
Msg := FLS(" Field1", 12) &
"|" & FLS(" Field2", 12) &
"|" & FLS(" Field3", 16);
Log_To_File("# " & Msg);
Log_To_File("# " & Fill_String(Msg'Last, '-'));
end;
A lot of folks who are used to the C way of building strings in steps have trouble wrapping their minds around Ada strings, which you are supposed to initialize and use as-is. When you grok this fact about Ada strings, the solution becomes much simpler. I can even throw out your "Fill" routine.
procedure F() is
Msg : constant String
:= FLS(" Field1", 12) &
"|" & FLS(" Field2", 12) &
"|" & FLS(" Field3", 16);
Separator : constant String := (1..Msg'length => '-'); --'
begin
Open_Log();
Log_To_File("# " & Msg);
Log_To_File("# " & Separator);
end;
(Note: The comment is a hack to get SO's colorizer back on track)
If you didn't have to have the separator the same length, you wouldn't even need to declare the variable.
If it were me, I'd do something like have Log_To_File
keep track of lengths and generate its own properly-sized separator upon request. Then you could just write:
Open_Log();
Log_To_File ("# " & FLS(" Field1", 12) &
"|" & FLS(" Field2", 12) &
"|" & FLS(" Field3", 16));
Log_Separator_To_File;
Just declare Msg as a String instead of a String(1 .. 1024)
procedure F() is
Msg: String
:= FLS(" Field1", 12) &
"|" & FLS(" Field2", 12) &
"|" & FLS(" Field3", 16);
--// this 'magically' declares Msg as a String(1 .. Something)
--// with the right Something
begin
Open_Log();
Log_To_File("# " & Msg);
Log_To_File("# " & Fill_String(Msg'Last, '-')); --'
end;
One approach might be to write a function that fills a fixed length string with a dynamically sized input string, padding with spaces:
procedure Pad_String(Str: in String; Dest: out String; Len: out Integer) is
begin
Len := Str'Last - Str'First + 1;
Dest(Dest'First .. Dest'First + Len - 1) := Str(Str'First .. Str'First + Len - 1);
Dest(Dest'First + Len .. Dest'Last) := Fill_String(Dest'Last - Len, ' ');
end Pad_String;
Ada's string handling allows you to pass any fixed length buffer into Dest
and the 'First
and 'Last
attributes will be correct within the body of the procedure.
Then, your code could become:
procedure F() is
Msg : String(1..1024);
Len : Integer;
begin
Open_Log();
Pad_String( FLS(" Field1", 12) &
"|" & FLS(" Field2", 12) &
"|" & FLS(" Field3", 16),
Msg,
Len);
Log_To_File("# " & Msg(1 .. Len));
Log_To_File("# " & Fill_String(Len, '-'));
end;
As a convenience, you can use the String constructor functions in Ada.Strings.Fixed
, Ada.Strings.Bounded
or Ada.Strings.Unbounded
. These overload the * operator to "replicate a character or string a specified number of times." For example,
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
...
Log_To_File("# " & Length(Msg) * '-');
I worked out how to use Unbounded_String. That type will accept other sized strings.
You can't build an unbounded string with the & operator unless you are using unbounded strings so use the To_Unbounded_String function.
with Ada.Strings.Unbounded;
procedure F() is
use Ada.Strings.Unbounded;
Msg : Unbounded_String;
begin
Open_Log();
Msg := Ada.Strings.Unbounded.To_Unbounded_String(
FLS(" Field1", 12) &
"|" & FLS(" Field2", 12) &
"|" & FLS(" Field3", 16));
Log_To_File("# " & Ada.Strings.Unbounded.To_String(Msg));
Log_To_File("# " &
Fill_String(Ada.Strings.Unbounded.Length(Msg), '-'));
end;
精彩评论