-
Notifications
You must be signed in to change notification settings - Fork 258
[SUGGESTION] Support for a "main
v2" signature
#262
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
main
v2" signaturemain
v2" signature
Thanks, and thanks again to everyone else who has made this suggestion in past issues. This has been on my mind as well. Yes, there's a safety aspect to this. Just as importantly, there's a simplicity aspect: Simplifying C++ includes addressing the things, even little things, that are constant annoyances and sources of friction and just distractions because we keep having to talk about them (and in many but not all cases have to teach them). That this keeps coming up shows that this is one of those things. I've already started implementing this a couple of times, and backed it out only because With the above checkin, the following works:
There can now be a single-parameter form of global If the programmer tries to write something like
Thanks again, everyone! |
Sorry for the duplicate! I tried to search for if someone had already suggested this and must've missed it! But very happy to see this change. :) Thanks Herb! |
@hsutter, one question regarding the new main: (argc:int, argv:**char) -> int = {
options : cxxopts::Options = ("execspec", "Executable specification - create docs that test your code");
options.add_options()
("i,input", "Input markdown", cxxopts::value<std::string>())
("o,output", "Save output to", cxxopts::value<std::string>())
("d,debug", "Enable debugging", cxxopts::value<bool>()*.default_value("false"))
("html", "Generate html", cxxopts::value<bool>()*.default_value("false"))
("h,help", "Print usage");
result := options.parse(argc, argv); // <----- here the lib uses argc & argv... and have no other methods available
if result.count("help") {
std::cout << options.help() << std::endl;
exit(0);
}
// ...
} Maybe we should provide a way to get argc & argv for some cases? I think it should not be the default, but for the sake of
I think we should provide it. I am asking here to clarify if I should post a bug report or if is it a feature that I need to learn to live with. |
I have realized that I can rewrite it to: main: (args) -> int = {
// ...
result := options.parse(argc, argv); // <---- argc & argv are available on the cpp1 side as implementation detail
// ...
} It will generate the following cpp1 code: [[nodiscard]] auto main(int argc, char **argv) -> int{
auto args = cpp2::args(argc, argv);
// ...
auto result {CPP2_UFCS(parse, options, argc, argv)};
// ...
} The question is if using implementation details is the solution we want to support. |
That's a good point... The named parameter But because under the covers cppfront generates the standard signature, you actually do have access to both
That said, I didn't actually realize that until you asked the question, so thanks! And I like it... it encourages the right thing (the visible named parameter is safe and the thing you naturally use), but writing a named parameter also makes Speaking of which... one thing that I haven't done, but have sometimes considered, is requiring the name of the single parameter to actually be |
Ah, racing replies -- you saw it too. Yes, I think it's important to guarantee those names' availability because you gave a great motivating example showing that it's part of full compatibility with all existing C and C++ libraries, which rely on What do you think of requiring the single-main-parameter name to be |
Most of us were using |
I like the idea of making Instead of a It looks like the code at #262 (comment) would generate an unused |
Last quick thought before going to sleep. Maybe we can do something like this: https://godbolt.org/z/s7bq33MT7 struct Args : std::vector<std::string_view> {
Args(int c, char **v) : vector{(size_t)c}, argc{c}, argv{v} {}
int argc = 0;
char** argv = nullptr;
};
inline auto args(int argc, char **argv) -> Args {
auto ret = Args{argc, argv};
for (auto i = 0; i < argc; ++i) {
ret[i] = std::string_view{argv[i]};
}
return ret;
}
|
Array requires the size to be known at compile time which is not the case for the |
Sorry for my replies, realised they weren't correct so deleted both of them. |
@filipsajdak I like your implementation strategy, and I'll pursue the direction of providing (Fun fact: Generating the main parameter name |
Or maybe you could ban those names in Cpp2's Perhaps I'm suggesting complications of unproven value. |
I'm wondering, if the signature must be exactly the magic |
@gregmarr Yes, I had the same thought, but then I thought it would be inconsistent with removing magic... I'm deliberately making |
I think that's mainly why I wasn't sure it was a great idea. I'm glad for the discussion to bring out why it's not a great idea. There is sill a little bit of magic if you require a specific variable name and that you must not specify its type, but I agree with everything you wrote about why having the two forms is a good idea. I'm still a little unsure about the exact parameter name though, as that does seem like more magic than we had before. |
@hsutter one more thing - there are other int main(int argc, char* argv[], char* envp[]);
int wmain(int argc, wchar_t* argv[], wchar_t* envp[]); I like the idea to require the name of the argument to be exactly
So, the cpp2 code can look like the following: main: (args, envs) = {
//...
} can generate: auto main(int argc_, char* argv_[], char* envp_[]) -> int {
[[maybe_unused]] auto args = Args{argc_, argv_};
[[maybe_unused]] auto envs = Envs{envp_};
// ...
} and for wmain: (args, envs) = {
//...
} can generate: auto wmain(int argc_, wchar_t* argv_[], wchar_t* envp_[]) -> int {
[[maybe_unused]] auto args = Args{argc_, argv_};
[[maybe_unused]] auto envs = Envs{envp_};
// ...
} I have created a prototype for template <typename CharT>
struct Args : std::vector<std::basic_string_view<CharT>> {
using Super = std::vector<std::basic_string_view<CharT>>;
Args(int c, CharT **v) : Super{(size_t)c}, argc{c}, argv{v} {
for (auto i = 0; i < argc; ++i) {
this->emplace_back(argv[i]);
}
}
int const argc;
CharT** const argv;
};
template <typename CharT>
struct Envs : std::vector<std::basic_string_view<CharT>> {
Envs(CharT **v) : envp{v} {
for (auto it = envp; *it; ++it) {
this->emplace_back(*it);
}
}
CharT** const envp;
}; |
@hsutter if you like the idea I can prepare a Pull request. Just let me know. |
Interesting ideas, thanks... Hmm. I worry about portability: I think Here's my hot take: Re Re
Is that sufficient, or is there a need for a thin STL-style wrapper to allow convenient and safer usage like The only WG21 proposal in this area I know of was P1275, which was encouraged but the author didn't come back with a revision to progress it. What's in cppfront now for |
I think that is sufficient. I post it to be sure that we have covered all possible cases regarding |
It's the other way around, so should be OK 🙂 |
This is an omnibus commit of the last few evenings' changes. Primarily it was to start laying the groundwork for constructors, but it includes other fixes and closes a few issues. Details: - Begin infrastructure preparation for constructors - Started creating navigation APIs to replace the low-level graph node chasing; this makes cppfront's own code cleaner and the tree easier to change if needed, but it's also a step toward a reflection API - Extended `main:(args)` to require the name "args" for the single-parameter `main`, and to support `args.argc` and `args.argv`, further on #262 (see comment thread) - Changed default return type for unnamed functions to be `-> void`, same as named functions, closes #257 - Disallow single-expression function body to be just `return`, closes #267 - Make `make_args` inline, closes #268 - Bug fix: emit prolog also for single-expression function body. Specifically, this enables main:(args)=expression; to work correctly. Generally, this also corrects the code gen for examples that were trying (but failing) to inject prologs in single-expression functions... in the regression tests this corrected the code gen for `pure2-forward-return.cpp2` which was missing the contract check before.
Only if the value in the dictionary value is something like an optional string view, as it needs to preserve the difference between null and an empty string.
|
Is that a thing people do with env variables? 😩 |
Yes, sometimes it is enough to have a variable set, without any particular content. |
I wonder if allocation is really needed for
A useful property of |
No, you need something to own the |
Right! 👍 |
It could be omitted using ranges. When ranges will be available on all platforms we can work on that further. |
Because my apple-clang-14 has just provided support for ranges, I prepared a PR that uses ranges to avoid allocating std::vector - see: #380 |
Thanks! I worry about that newness though -- is it worth discarding compatibility with last month's Apple Clang to save a single allocation? (will repeat this comment on the PR) |
Thanks. Sorry for the trouble. |
This is an omnibus commit of the last few evenings' changes. Primarily it was to start laying the groundwork for constructors, but it includes other fixes and closes a few issues. Details: - Begin infrastructure preparation for constructors - Started creating navigation APIs to replace the low-level graph node chasing; this makes cppfront's own code cleaner and the tree easier to change if needed, but it's also a step toward a reflection API - Extended `main:(args)` to require the name "args" for the single-parameter `main`, and to support `args.argc` and `args.argv`, further on hsutter#262 (see comment thread) - Changed default return type for unnamed functions to be `-> void`, same as named functions, closes hsutter#257 - Disallow single-expression function body to be just `return`, closes hsutter#267 - Make `make_args` inline, closes hsutter#268 - Bug fix: emit prolog also for single-expression function body. Specifically, this enables main:(args)=expression; to work correctly. Generally, this also corrects the code gen for examples that were trying (but failing) to inject prologs in single-expression functions... in the regression tests this corrected the code gen for `pure2-forward-return.cpp2` which was missing the contract check before.
The usual
argc
/argv
signature requires familiarity some C idioms that don't really have broad influence on C++. An alternative signature could allow new learners to not be bogged down by the dubiouschar**
and\0
termination, and would instead provide them with a clearer, more familiar data type, that would be less error and abuse-prone. Ideally it would only take a single argument,args
, which would be a standard list container, holding some number of arguments. Perhapsmain : (args: std::vector<std::string_view>) -> int
.Will your feature suggestion eliminate X% of security vulnerabilities of a given kind in current C++ code?
I've seen some wild forms of argument parsing out there that abuse
argc
. The percentage would definitely be small, but I'm sure there are people out there misusingargc
enough to have allowed a memory safety bugs to slip in.Will your feature suggestion automate or eliminate X% of current C++ guidance literature?
It's largely agreed that passing a length and a C array is bad practice when writing your own functions, but you don't get to dictate the signature of
main
. Requiringmain
to follow an idiom that is otherwise frowned upon (and in a place that every C++ programmer is bound to come across!) raises a lot of questions about why things are this way, and why it's a bad idea to use a signature likemain
's elsewhere in your code. Ifmain
took something a little more idiomatic, those conversations can be kicked down the road until much later, instead of requiring they be addressed while trying to write a "Hello, world!"Some references:
std::vector<std::string_view>
immediately, and then only using that.serenity_main
function with a nicer type signature, instead of writing the usualmain
function.Describe alternatives you've considered.
Some potential types that could be used for
args
std::array<_>
std::vector<_>
_<std::string>
_<std::string_view>
The text was updated successfully, but these errors were encountered: