Then what's the point of allowing multiple candidates? The compiler will just choose one anyway.
There is no need for new language construct to support that:
#if FLAG
func main() { … }
#else if FLAG2
func main() { … }
#endif
Yes, I completely missed that! It would be great to clarify that in the writing and spell out the implications. The proposal makes a lot more sense with this Nate, thank you!
I think I made the same concern during a review for one of these. The problem is each one of these is like a protocol, but have a quirk that protocols don't cover. If the language was more mutable, then we could develop a superior protocol concept that dC
, dMA
, and pW
could template, and therefore could be removed as built-ins. We need to start with generalizing protocols further.
I'm not sure if this is what you mean, but let me turn this into a question: what happens when this attribute is applied to a protocol? Do conforming types automatically become "main like"? If so, this could be very nice.
The attribute is only applicable to types — it would be an error to apply @main
to a protocol. A protocol can—and would, in basically all cases I can think of—supply the static func main()
requirement for the type that is declared with @main
. As the proposal describes, the compiler enforces the rule that only a single type is the designated entry-point by making it an error to apply @main
to more than one type.
Ok, why not allow the attribute on a protocol? Of course, only one type could conform to the protocol in a program, but that seems like a natural way to provide the default implementation. I don't see why it is important to have the attribute here:
@main
struct X : SomeObviouslyMainlikeProtocol {
instead of on the protocol itself.
In any case, if this is a bad idea, it would be great to mention it in the alternatives considered section of the proposal. Thanks!
@Chris_Lattner3’s last point is what I was thinking the entire time reading through this thread. Unless there are reasons why this Is not a good idea, it seems more natural to encapsulate all of the required functionality to get a framework up and running into a protocol defined by the framework.
Despite that, I’m not sure how I feel about the pitch overall.
I do support the idea of this pitch when it comes to generalizing the existing @UIApplicationMain
and @NSApplicationMain
attribute into something useful for other frameworks, but I think making the behavior automatic when conforming to a protocol goes a bit too far.
Take NSApplicationDelegate
for instance. If the protocol had the @main
attribute, it makes it impossible to create an application delegate that doesn't have its own main. That'd be a bit problematic if you are implementing main
in a separate file (maybe you want to write main
in Objective-C). Or should we add a way to "un-main" your type?
Honestly, I'm annoyed at the idea that conforming to a protocol would emit a main
by itself. That this happens isn't obvious at all. It's hard to opt-out, and the way you can "override and call the super implementation" is pretty awkward.
This is a good explanation of why I don’t think allowing @main
on protocols is the direction we want. @Chris_Lattner3 I’ll add a note to the proposal on this point!
+1 thanks Nate. FWIW, people felt the same way about dynamicMemberLookup (requiring an attribute on the class) but that got backpeddled later.
The difference I see between dynamicLookup
and main
is that the former is a localized API property of the type with a dynamicMember subscript, whereas main
is a global property, and there can only be one. It doesn't seem unreasonable that a large program might have multiple *ApplicationDelegate
classes for some reason, with only one being the entry point, or that a tools framework like LLVM might have multiple entry-point-like classes that serve as entry points for different tools, so having protocol conformance automatically occupy the one available entry point slot seems undesirable.
Are you saying that it seems the API is clumsy because something that shouldn’t be possible is? Or are you saying that something that should be possible is awkward and roundabout to accomplish?
(In my mind, the ability to “override and call super” is a useful feature. It’s why I find myself still using NSApplicationMain(_:_:)
instead of @NSApplicationMain
.)
I meant that if you're shadowing main()
with our own main()
inside a type that conforms to the protocol, you calling the protocol's extension method must be done this way:
(self as NSApplicationDelegate.Type).main()
That'd be the equivalent of calling NSApplicationMain(_:_:)
, except it's a lot less clear what this is.
And if the protocol has an associated type...?
Yep, that makes sense to me, I agree that the global effect is different. I was just asking that this get captured in the proposal.
-Chris
@compnerd — I took a look at this previous thread on entry points so that I could understand the different things at play, and as you describe there, there seem to be two dimensions of platform-specific differences at work.
On the one hand, there's a difference in the kinds of arguments that are available to an entry point, like the typical argument count and vector, the Windows-specific HINSTACE
, and the auxiliary vector on Linux. Some of these we provide right now as globally available properties through the stdlib's CommandLine
type, and the proposal calls out a future direction of letting libraries define main
as a (argc: Int, argv: ...) -> Int
function instead of a nullary one. So for the other platform-specific values we could provide access to them in one, or preferably both, of those locations.
On the other hand, Windows apps have a distinction between how GUI and console apps are launched based on whether you provide a wmain
or wWinMain
entry point, which isn't specifically addressed in the proposal. I see a couple different ways we could go about handling this:
-
We could expand the allowed static methods that a type can provide to include
windowsMain()
and/orwindowsMain(argc: Int, argv: ...)
. If the@main
designated type provides one of these, then the compiler would generate awWinMain
entry point instead of the defaultwmain
. -
We could allow parameters on the
@main
attribute, so that you could write something like this, which would look for amain(console: Bool)
ormain(console: Bool, argc: Int, argv: ...)
method:@main(console: false) struct MyApp: Application { // ... }
If the libraries that provide @main
-supporting application-root protocols generally support either GUI or console apps, but not really both, #1 would seem like an appropriate solution. If we anticipate that libraries will want to support both kinds of development (which seems unlikely to me), then #2 might make more sense. #1 would still work in the second case, however — the library would only need to supply different protocols for console and GUI apps.
One issue with #2 is that it would look like things are more flexible than they are, since this is borrowing a bit of the way property wrappers work with wrappedValue
. It would be a further extension of this syntax to support arbitrary library-defined parameters on the @main
attribute.
What do you think of these as potential future extensions of the @main
approach? Is this a correct summary of the platform-specific issues we need to address?
If we were to eventually adopt extension #1, you could add the following extension to your swift-win32
library. Then you could use the plain @main
attribute on your SwiftApplicationDelegate
type in your HelloSwift example.
extension AppDelegate {
public static func windowsMain(
_ argc: Int32,
_ argv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>
) -> Int32 {
ApplicationMain(argc, argv, nil, self)
}
}
Thanks @nnnnnnnn for following up on this.
I like the two separate domains of concerns that you have drawn, and is similar to how I am thinking about this problem space as well and I think that it is covering the areas that I wanted to really have considerations for, so in terms of your question of is this a correct summary: yes.
I think that the differences lie in the implementation detail only, and so, lets take each point individually about that.
The differences in arguments (Signature)
I think that the part that I am getting tripped up on here, which is probably my own misunderstanding, is what controls this signature. I would like that made a bit more explicit in the proposal.
My opinion is that we want to remove this from the compiler and instead place it into the purview of libraries. This allows us to have the ability to easily control the signature within frameworks. Doing so means that if it were deemed useful for a particular class of applications that the desired signature on Linux is void (int, char **, char **, void *)
for example, then the framework does so without any impact to the the rest of the platforms.
The benefit is that this scales nicely with freestanding (embedded) environments exposing a void (void)
to even android which actually would need something completely custom because the JNI setup and teardown must occur around this setup.
UI vs Console (Subsystem)
Windows has a different well defined signature - wWinMain
for UI applications. However, android has a separate issue where the entry point must be written in C with a stub to ensure that the JVM is initialized before main
. This is another case where the console application and the UI application startup behaviour is different.
If the entry point is controllable by the (executable) module being built, I think that we could get away with even just @main
or @ApplicationMain
.
I do think that providing the possibility of a single module providing both the console and the UI development is really interesting - the only caveat would be that only a single one would be active at any given time. The idea is similar to how the Microsoft C Runtime allows for both the console and the UI application entry points based upon the symbols you use. A similar distinction here would be convenient from the user perspective of, I want to experiment with an application and I can progress from it being a console to a UI application.
I'm left wondering if we could optionally just take a parameter (or a pair) which tells us what the signature and spelling of the entry point is. The invoked method would be required to have that same signature. By making it optional, I hope that most people would not even have to consider it when writing their applications.
I hope that is somewhat coherent.
Thanks,
Saleem
To keep the user-facing language feature uncluttered, maybe the attribute could live on the main
method implementation, so you could have something like:
// In the framework code
extension WindowsUIAppDelegate {
@entryPoint(symbolName: "wWinMain", convention: stdcall)
static func main(_ hInstance: HINSTANCE, _ hPrevInstance: HINSTANCE, lpCmdLine: LPWSTR, _ nCmdShow: Int32) { ... }
}
// In the user code
@main
class MyAppDelegate: WindowsUIAppDelegate { ... }
@Joe_Groff, interesting idea, I don't have a strong opinion on whether we split it this way or keep it together.