comparing with c strings
Let's say I have a D application, with some callbacks from C, where some arguments to the callbacks are `const char* path`. What is the recommended way to compare them to D strings? Without making allocations, if that's possible
Re: cannot find source code for runtime library file 'object.d'
On Monday, 20 November 2023 at 07:50:22 UTC, thinkunix wrote: denis via Digitalmars-d-learn wrote: ``` $ zypper install dmd $ dmd main.d Error: cannot find source code for runtime library file 'object.d' dmd might not be correctly installed. Run 'dmd -man' for installation instructions. config file: /etc/dmd.conf I would say the package has files in the wrong locations. Try the binary from dlang.org: https://downloads.dlang.org/releases/2.x/2.105.3/dmd-2.105.3-0.openSUSE.x86_64.rpm I'm not familiar with zypper but I'll bet you can install it by: # zypper install ./dmd-2.105.3-0.openSUSE.x86_64.rpm On a RHEL-like distro, this works: # yum install ./pkgname.rpm ... scot Thank you Scot, I confirm that installing manually does the trick Regards Denis
cannot find source code for runtime library file 'object.d'
``` $ zypper install dmd $ dmd main.d Error: cannot find source code for runtime library file 'object.d' dmd might not be correctly installed. Run 'dmd -man' for installation instructions. config file: /etc/dmd.conf $ cat /etc/dmd.conf ; ; dmd.conf file for dmd ; ; dmd will look for dmd.conf in the following sequence of directories: ; - current working directory ; - directory specified by the HOME environment variable ; - directory dmd resides in ; - /etc directory ; ; Names enclosed by %% are searched for in the existing environment and inserted ; ; The special name %@P% is replaced with the path to this file ; [Environment32] DFLAGS=-I/usr/include/dlang/dmd -L-L/usr/lib -L--export-dynamic -fPIC [Environment64] DFLAGS=-I/usr/include/dlang/dmd -L-L/usr/lib64 -L--export-dynamic -fPIC $ ls /usr/include/dlang/dmd $ cat /etc/os-release NAME="openSUSE Tumbleweed" VERSION="20231006" $ dmd --version DMD64 D Compiler v2.105.3 ``` Help?
Re: foreach iterator with closure
To keep this reply brief, I'll just summarize: Lots of great takeaways from both of your posts, and a handful of topics you mentioned that I need to dig into further now. This is great (I too like D :) I very much appreciate the extra insight into how things work and why certain design decisions were made: for me, this is essential for gaining fluency in a language. Thanks again for all your help! Denis
Re: foreach iterator with closure
Many thanks: your post has helped me get past the initial stumbling blocks I was struggling with. I do have a followup question. First, here are my conclusions up to this point, based on your post above, some additional experimentation, and further research (for future reference, and for any other readers). * foreach is the actual iterator, the instantiation of a struct is the range. * When a constructor is not used, the arguments in the call to instantiate the range (in this case, `hello` in letters(`hello`)) are mapped sequentially to the member variables in the struct definition (i.e. to letters.str). * When a constructor is used, the member variables in the struct definition are in essence private. The arguments in the call to instantiate the range are now mapped directly to the parameters in the definition of the "this" function. * The syntax and conventions for constructors is difficult and non-intuitive for anyone who hasn't learned Java (or a derivative). The linked document provides a simplified explanation for the "this" keyword, which is helpful for the first read: https://docs.oracle.com/javase/tutorial/java/javaOO/thiskey.html. * In some respects, the Java syntax is not very D-like. (For example, it breaks the well-established convention of "Do not use the same name to mean two different things".) However, it does need to be learned, because it is common in D source code. Here is the complete revised code for the example (in condensed form): import std.stdio; struct letters { string str; int pos = 1;// Assign here or in this()) this(string param1) { // cf. shadow str str = param1; // cf. this.str = param1 / this.str = str writeln(`BEGIN`); } char front() { return str[pos]; } void popFront() { pos ++; } bool empty() { return pos == str.length; } ~this() { writeln("\nEND"); }} void main() { foreach (letter; letters(`hello`)) { write(letter, ' '); }} At this point, I do have one followup question: Why is the shadow str + "this.str = str" the more widely used syntax in D, when the syntax in the code above is unambiguous? One possible reason that occurred to me is that "str = param1" might require additional GC, because they are different names. But I wouldn't think it'd make any difference to the compiler. Denis
foreach iterator with closure
Is it possible to write an iterator that does the following, using a struct and some functions? - Operates in a foreach loop - Has BEGIN-like and END-like blocks or functions that are executed automatically, before and after the iterations - Initializes variables in the BEGIN block that are used in the other two. These variables are for internal use only, i.e. must not be accessible to the user of the foreach loop I'd like to use the simplest solution while keeping the code clean. As a starting point, here's a contrived example using a struct with a range-style iterarator: import std.stdio; struct letters { string str; int pos = 0; char front() { return str[pos]; } void popFront() { pos ++; } bool empty() { if (pos == 0) writeln(`BEGIN`); else if (pos == str.length) writeln("\nEND"); return pos == str.length; }} void main() { foreach (letter; letters(`hello`)) { write(letter, ' '); } writeln(); } The obvious problems with this code include: (1) The user can pass a second argument, which will set the initial value of pos. This must not be allowed. (The real code will need to initialize a half dozen internal-only variables, and do some additional work, before the looping starts.) (2) Sticking the code for the BEGIN and END blocks into the empty() function is ugly. Can this iterator be written using a range-style struct? Or is something more complicated needed, like an OO solution? I should add that the final version of this will be put in a separate module, possibly in a library, so I can call it from many programs. Not sure if that might help simplify things. Thanks for your guidance.
Re: Calling C functions
On Friday, 26 June 2020 at 08:15:27 UTC, Jacob Carlborg wrote: On Friday, 26 June 2020 at 00:30:22 UTC, Denis wrote: extern(C) void cfunc(void function(int)); extern(C) void dcallback(int x) {...} <-- Why extern(C)? cfunc(); Can this be rewritten, dropping the prefix from the second line? If not, it would be helpful to know why "extern(C)" is needed here too. No, it cannot be dropped. `extern(C)` is required because C and D are using different calling conventions (D functions are also mangled). For example, D (at least DMD and LDC) are passing the arguments to the function in reverse. OK, now this makes sense. I tested calling the same callback function directly from D: it compiled and worked correctly. So at least prefixing the callback function with `extern(C)` doesn't prevent the rest of the D program from calling it too. (2) Is there a way to restrict the invocation of a linked C function to one specific D function? [...] For functions nested in a D language construct (class, struct, function) the compiler will always use the D mangling, instead of the C mangling. In theory it would be possible to workaround that by forcing the mangled name using `pragma(mangle)`, but for some reason the compiler doesn't allow `pragma(mangle)` inside a function body, on a nested function declaration. You can wrap up everything in a struct, as follows: I see. Thank you very much for these explanations and code -- the insights are very helpful. Denis
Calling C functions
I have a two questions about calling C functions from D. (1) When passing a D callback to a C function, is there a way to write the code without having to prefix the callback declaration with "extern(C)"? It's not a big deal adding the prefix to the D function declaration. It just seems odd to me to prefix D code with "extern(C)". For example, the following code works: extern(C) void cfunc(void function(int)); extern(C) void dcallback(int x) {...} <-- Why extern(C)? cfunc(); Can this be rewritten, dropping the prefix from the second line? If not, it would be helpful to know why "extern(C)" is needed here too. (2) Is there a way to restrict the invocation of a linked C function to one specific D function? If the C header is defined in one of the core.stdc libraries, the import statement can either be global or inside a specific D function -- both work. In contrast, when the C function prototype is written directly into the D program (as above), the linker complains unless this declaration is made global. If it's possible to restrict the scope of the C function to just one D function, I'll take advantage. (I'm using dmd, if that makes a difference.) Thanks
Re: Flagging special conditions on return from a function call
On Tuesday, 23 June 2020 at 21:34:25 UTC, Paul Backus wrote: If you're open to using Dub packages [...] Because this is going to be used in almost every program I write, I need to eliminate outside dependencies as an option. Nonetheless, thanks for this suggestion. [2] https://pbackus.github.io/blog/beating-stdvisit-without-really-trying.html I did read your blog post: that's an impressive result. Good to know that well-written D code can be competitive with C. Denis
Re: Flagging special conditions on return from a function call
Perhaps this thread would have been better titled "Returning a value and a status", and the question phrased as "What are your preferred techniques?". I'm planning to port some of my programs to D, so I'd like to standardize on one or two techniques for handling this very common situation, in a way that suits the language. I'd appreciate hearing from others about what you do. --- On Tuesday, 23 June 2020 at 06:52:29 UTC, Simen Kjærås wrote: On Tuesday, 23 June 2020 at 04:01:45 UTC, Denis wrote: [...] (4) Set the function return value normally, and put the flag in an "out" variable passed as an argument to the function (5) Return the flag, and put the value in an "out" variable passed to the function (i.e. the reverse of #4) Both of these happen. I don't know which is more common. In C# these are probably the most common way (except for exceptions) to signal these cases. Good to know. Both of these techniques (or something similar) are used in Perl too. The choice seemed to depend on which one the author thought would be more useful as the return. [...] by presenting an interface that only compiles when both cases are covered, like fun().match((T t) => t, () => Error()). A complete solution wrapped in a tidy package -- I like it. Thanks for sharing.
Flagging special conditions on return from a function call
Is there a preferred idiom in D for flagging special conditions when returning from a function call? Here "special conditions" refers to expected situations (e.g. reaching the end of something, like EOF) rather than outright errors (so exception-try-catch is inappropriate). I've come across many ways for a function to return both a value and flag, including: (1) Assign an unused value for the flag (e.g. -1 when the function returns an int), and return the combined value/flag. (2) Return a tuple with the value and the flag (3) Return a struct or tuple with named value and flag members (4) Set the function return value normally, and put the flag in an "out" variable passed as an argument to the function (5) Return the flag, and put the value in an "out" variable passed to the function (i.e. the reverse of #4) (6) Use two separate functions, one that returns the value, and another that can be called afterwards to check the flag (like eof(), for example) (7) Use a side effect and set a designated global variable I'm sure there are others. * Is there a preferred approach? * Which ones are discouraged? * General recommendations or guidelines? If there is a best practice, I'd rather learn it sooner than many lines of code later. (In the interest of brevity, I'll limit my own comments in this post to the following: In the past, I've tried to adhere to KISS. This means that I would generally prefer #1. But often it isn't possible to combine the value and flag that way, in which case one of the other methods must be used.)
Re: Some questions about strings
On Monday, 22 June 2020 at 09:06:35 UTC, Jacob Carlborg wrote: String **literals** have a terminating null character, to help with integrating with C functions. But this null character will disappear when manipulating strings. You cannot assume that a function parameter of type `string` will have a terminating null character, but calling `printf` with a string literal is fine: printf("foobar\n"); // this will work since string literals have have a terminating null character OK, it makes sense that the null terminator would be added where compatability with C is required. Good to know.
Re: Some questions about strings
On Monday, 22 June 2020 at 04:32:32 UTC, Mike Parker wrote: On Monday, 22 June 2020 at 04:08:10 UTC, Denis wrote: On Monday, 22 June 2020 at 03:31:17 UTC, Ali Çehreli wrote: : string is char[] wstring is wchar[] dstring is dchar[] Got it now. This is the critical piece I missed: I understand the relations between the char types and the UTF encodings (thanks to your book). But I mistakenly thought that the string types were different. They're aliases in object.d: https://github.com/dlang/druntime/blob/master/src/object.d#L35 Right at the top and plain as day too... ;) I appreciate the link to the source -- thanks!
Re: Some questions about strings
On Monday, 22 June 2020 at 03:31:17 UTC, Ali Çehreli wrote: : string is char[] wstring is wchar[] dstring is dchar[] Got it now. This is the critical piece I missed: I understand the relations between the char types and the UTF encodings (thanks to your book). But I mistakenly thought that the string types were different. You can reveal some of the mystery by looking at their .length property. Additionally, foreach will visit these types element-by-element: char, wchar, and dchar, respectively. I did not try this test -- my bad. null character is not required but may be a part of the strings. The terminating null character was one of the reasons I thought strings were different from char arrays. Now I know better. Thank you for these clarifications. Denis
Re: Some questions about strings
On Monday, 22 June 2020 at 03:49:01 UTC, Adam D. Ruppe wrote: On Monday, 22 June 2020 at 03:43:58 UTC, Denis wrote: My code reads a UTF-8 encoded file into a buffer and validates, byte by byte, the UTF-8 encoding along with some additional validation. If I simply return the UTF-8 encoded string, there won't be another decoding/encoding done -- correct? Yeah D doesn't do extra work when you are just passing stuff around, only when you specifically ask for it by calling a function or maybe doing foreach (depends on if you ask for char or dchar in the foreach type) Excellent. I'm trying to make this efficient, so I'm doing all of the validation together, without using any external functions (apart from the buffer reads). Thanks!
Re: Some questions about strings
On Monday, 22 June 2020 at 03:24:37 UTC, Adam D. Ruppe wrote: On Monday, 22 June 2020 at 03:17:54 UTC, Denis wrote: - First, is there any difference between string, wstring and dstring? Yes, they encode the same content differently in the bytes. If you cast it to ubyte[] and print that out you can see the difference. - Are the characters of a string stored in memory by their Unicode codepoint(s), as opposed to some other encoding? no, they are encoded in utf-8, 16, or 32 for string, wstring, and dstring respectively. - Can a series of codepoints, appropriately padded to the required width, and terminated by a null character, be directly assigned to a string WITHOUT GOING THROUGH A DECODING / ENCODING TRANSLATION? no, they must be encoded. Unicode code points are an abstract concept that must be encoded somehow to exist in memory (similar to the idea of a number). OK, then that actually simplifies what's needed, because I won't need to decode the UTF-8, only validate it. My code reads a UTF-8 encoded file into a buffer and validates, byte by byte, the UTF-8 encoding along with some additional validation. If I simply return the UTF-8 encoded string, there won't be another decoding/encoding done -- correct?
Some questions about strings
I have a few questions about how strings are stored. - First, is there any difference between string, wstring and dstring? For example, a 3-byte Unicode character literal can be assigned to a variable of any of these types, then printed, etc, without errors. - Are the characters of a string stored in memory by their Unicode codepoint(s), as opposed to some other encoding? - Assuming that the answer to the first question is "no difference", do strings always allocate 4 bytes per codepoint? - Can a series of codepoints, appropriately padded to the required width, and terminated by a null character, be directly assigned to a string WITHOUT GOING THROUGH A DECODING / ENCODING TRANSLATION? The last question gets to the heart of what I'd ultimately like to accomplish and avoid. Thanks for your help.
Re: Reading text (I mean "real" text...)
Digging into this a bit further -- POSIX defines a "print" class, which I believe is an exact fit. The Unicode spec doesn't define this class, which I presume is why D's std.uni library also omits it. But there is an isprint() function in libc, which I should be able to use (POSIX here). This function refers to the system locale, so it isn't limited to ASCII characters (unlike std.ascii:isPrintable). So that's one down, two to go: Loop until newline or EOF (1) Read bytes or character } Possibly (2) Decode UTF-8, exception if invalid } together (3) Call isprint(), exception if invalid Return line (This simplified outline obviously doesn't show how to deal with the complications arising from using buffers, handling codepoints that straddle the end of the buffer, etc.) Where I'm still stuck is the read or read-and-auto-decode: this is where the waters get really muddy for me. Three different techniques for reading characters are suggested in this thread (iopipe, ranges, rawRead): https://forum.dlang.org/thread/cgteipqqfxejngtpg...@forum.dlang.org I'd like to stick with standard D or C libraries initially, so that rules out iopipe for now. What would really help is some details about what one read technique does particularly well vs. another. And is there a technique that seems more suited to this use case than the rest? Thanks again
Re: Reading text (I mean "real" text...)
On Saturday, 20 June 2020 at 01:41:50 UTC, Paul Backus wrote: It sounds like maybe what you are looking for is Unicode character categories: https://en.wikipedia.org/wiki/Unicode_character_property#General_Category The character validation step could indeed be expressed using Unicode properties: Allow Unicode White_Space Reject Unicode Control Allow everything else
Reading text (I mean "real" text...)
THE PROBLEM UTF-8 validation alone is insufficient for ensuring that a file contains only human-readable text, because control characters are UTF-8 valid. Apart from tab, newline, carriage return, and a few less commonly used others considered to be whitespace, human-readable text files should not normally contain embedded control characters. In the standard library, the read functions for "text" files (as opposed to binary files) that I looked at are not actually based on "human readable text", but on "characters". For example: - In std.stdio, readln accepts everything. Lines are simply separated by the occurrence of a newline or user-designated character. - In std.file, readText accepts all valid UTF-8 characters. This means, for example, that all of these functions will happily try to read an enormous file of zeroes in its entirety (something that should not even be considered "text") into a string variable, on the very first call to the read function. Not good... Whereas a function that reads only "human-readable text" should instead generate an exception immediately upon encountering an invalid control character or invalid UTF-8 character. THE OBJECTIVE The objective is to read a file one line at a time (reading each line into a string), while checking for human-readable text character by character. Invalid characters (control and UTF-8) should generate an exception. Unless there's already an existing function that works as described, I'd like to write one. I expect that this will require combining an existing read-by-UTF8-char or read-by-byte function with the additional validation. Q1: Which existing functions (D or C) would you suggest leveraging? For example, there are quite a few variants of "read" and in different libraries too. For a newcomer, it can be difficult to intuit which one is best suited for what. Q2: Any source code (D or C) you might suggest I look at, to get ideas for how parts of this could be written? Thanks for your help.
Re: "if not" condition check (for data validation)
On Thursday, 18 June 2020 at 17:57:49 UTC, Ali Çehreli wrote: Here is an earlier experiment of nested templates, which may be useful in this case. : I think you should be able to pass callables as 'alias' template arguments Sounds good. This gives me an opportunity to learn how nested templates can be used. `alias` in this context is also new, so I'll dig into that too. Thanks!
Re: "if not" condition check (for data validation)
On Thursday, 18 June 2020 at 12:50:35 UTC, Stanislav Blinov wrote: No, there isn't a way to write an operator. OK, first choice eliminated. On Thursday, 18 June 2020 at 13:57:39 UTC, Dukc wrote: No reason to use templates here pragma(inline, true) auto not(bool cond) { return !cond(); } I like it. The inline pragma eliminates the extra overhead of the function call, which was another objective. (And it introduces me to D's pragmas too.) Personally, I find this: if ( not( abra && cadabra )) ... to be more clear than: if ( !( abra && cadabra )) ... I guess it depends on what one is used to. I do recognize that this would be non-standard for D, but I'm still going to use it because I find it more readabile. I should add that this one made me laugh though, giving flashbacks to that horrible "not speak" of the early 90s: if ( configfile.isFile.not ) ... LOL I've learned multiple things from this posting. Thank you all for sharing your suggestions. Denis
Re: "if not" condition check (for data validation)
Let me clarify the requirements and rephrase the question -- REQUIREMENTS `assert`, `enforce` and the `unless` function I wrote (above) all allow the condition to be expressed in an affirmative sense, and they also achieve the goal of readability. These are the initial requirements. `unless` improves on `assert` and `enforce` by allowing a custom action to be specified. This might be to write an error message and exit (like `assert` and `enforce`). But it could also be to write a warning message (to console or a log file) but not exit. So the next requirement is to be able to specify a custom action. Unfortunately a function, like the one I wrote, doesn't work because it doesn't allow actions that would skip or repeat some code (`continue`, `goto`, etc) to be specified as the second argument. It also doesn't allow code to be skipped or repeated after executing the function in the second argument. (That is, after writing a warning message or log notice, continue processing at some point.) A conditional operator like `if` is needed, not a function. THE ESSENTIAL QUESTION Is there a way to write an `unless` operator that would allow the condition to be expressed in an affirmative sense? It would be used like `if`, i.e. something like: unless ( ) { ; // Or even: continue; } Templates offer a clean syntax, but I can't come up with a way to use them for a conditional operator. Mixins are flexibile, but I suspect the result would not be very readabile (that is, less readable even than "if ( ! (..." ). I was hoping that some feature, or combination of features, in D might allow this to be achieved.
Re: "if not" condition check (for data validation)
On Thursday, 18 June 2020 at 00:43:40 UTC, Stanislav Blinov wrote: if( ! (configfile.isFile && configfile.extension == ".conf") ) ? That does indeed clean up the compound logic. One is still left with: if( !( if( ! if( !( if( ! : I was hoping to get away from all the `not`s too.
"if not" condition check (for data validation)
Is there a cleaner way to implement an "if not" condition check? WHY NOT JUST USE "IF"? For data validation, code is cleaner and more intelligible if the condition being checked is written in an affirmative sense; that is, in the same way that `assert` is written. This is especially true when `and` and `or` logic is involved. `if` is not a good substitute, because it works in the opposite sense, often requiring lots of `not`s. As a trivial example: assert( configfile.isFile && configfile.extension == ".conf" ) -vs- if ( !configfile.isFile || configfile.extension != ".conf" ) An `if` statement can be written in the affirmative sense, by using an empty `then` statement + an `else` statement: if ( configfile.isFile && configfile.extension == ".conf", message ) { } else But then the logic intuitively feels wrong for an `if`, because the handler is always in the `else`. When there are only a few such checks, it might not matter. But when there are a lot of checks, the code gets ugly (lots of `else`s) and the clutter adds up. ORIGINAL SOLUTION The following solution works and the code is very readable. However, it has a couple of notable deficiencies. void unless(T)(T condition, lazy void func ) { if ( !condition ) func(); } : unless( configfile.isFile && configfile.extension == ".conf", handleIt( _ _ )); The most obvious shortcomings are: 1. It only works with a handler function. So `continue` and the like can't be used, for example. 2. It is inefficient, adding an extra function call to every condition check. Inside a loop, this is cumulative. A BETTER SOLUTION ??? I haven't been able to come up with another option that is more efficient yet doesn't sacrifice readability. I would welcome suggestions. Thanks in advance. Denis
Re: Initializing an associative array of struct
On Sunday, 14 June 2020 at 15:44:04 UTC, Ali Çehreli wrote: On 6/14/20 7:43 AM, Denis wrote:> @Kagamin: > > On Sunday, 14 June 2020 at 07:16:18 UTC, Kagamin wrote: >> parameters[param]=Parameter(); > > I did not realize that you can use a type on the RHS of an assignment, Note that it's not just the type but with parenthesis after it. For example, Foo() default-constructs an object of Foo. Got it. > There does not appear to be a way to loop over the elements of an enum, There is std.traits.EnumMembers: import std.traits; enum Foo { abc, xyz } void main() { foreach (foo; EnumMembers!Foo) { // ... } } Excellent. Because all of the keys are immutable strings and are known at compile time, enum seems like it should be well suited for the job. I will try this too. On a side note: I am working my way through your book, Ali. It really is very helpful for learning D, and for pointing out things that are not always clear from reading the documentation alone. I will admit, however, that I am not able to absorb all of the more subtle aspects yet. So this is an iterative process (read some, write code, re-read and read some more, write code, ...) Thank you for your help.
Re: Initializing an associative array of struct
@Kagamin: On Sunday, 14 June 2020 at 07:16:18 UTC, Kagamin wrote: parameters[param]=Parameter(); I did not realize that you can use a type on the RHS of an assignment, but it is clear and succinct. This syntax will be very useful going forward -- thank you. @Stanislav B: On Sunday, 14 June 2020 at 09:34:08 UTC, Stanislav Blinov wrote: string[] keys = [ "huh", "buh", "hrm", "pff", "err", "ack", "ugh", /* ... */ "zzz" ]; foreach (k; keys) parameters.require(k); This solves the problem of initializing the keys perfectly -- thank you. There does not appear to be a way to loop over the elements of an enum, so a string array does make the most sense as the initial container for the keys. I should add that your explanation for using `require` to perform the init is much more clear than the documentation! The `require` function, when called only with key, will insert default (.init) value for that key. I am just starting to learn D, and am missing so many pieces of the puzzle that I'm not yet able to always put "two and two" together myself. This kind of help really goes a long way. Again, my thanks to you both. Denis
Initializing an associative array of struct
Hi, I'm trying to combine a couple of general types (associative array, struct) in a compound data structure, but without success. Here is what I'm trying to achieve: "param" is a string variable "parameters[param].id" is an integer value "parameters[param].value" is a string value param can be any of a fixed set of string values, all literals known at compile time. parameters needs to be pre-populated with all of its keys during initialization, because they will be used to validate the param values that are read from a file (by checking if "param in parameters" is true). I started with the following declarations: struct Parameter { int id; string value; } Parameter[string] parameters; string param; But then I simply could not come up with a way to initialize the keys of parameters. I tried using assignments as well as enum, but always received either a compile or a runtime error. (Unfortunately the error messages did not enable me to find a solution.) Note also that the defaults for id and value are fine (int and string defaults). But is it even possible to add the keys alone to parameters, without assigning something? I would welcome a suggestion for how to initialize the keys of parameters. As there will be a couple dozen of the param string keys, a more succinct method would be preferable over a verbose one. I realize that this is a very basic question, but I'm stumped! Thank you in advance for your help.