This is something I've toyed with in the past, but only recently considered using on a regular basis. I'll post a working example of some current brainstorm sessions, but until then, Id' like to know what you guys think about this approach?
Printable View
This is something I've toyed with in the past, but only recently considered using on a regular basis. I'll post a working example of some current brainstorm sessions, but until then, Id' like to know what you guys think about this approach?
What exactly do you have in mind? You would still need the main function, methinks.
I think it's quite good to have as little code as possible outside of classes. In my code, that usually means one function: main.
Normally, this function only has a try block with a call of a class function, and a catch block for all uncaught exception. I always catch std::exception and "..." and output a friendly message; sometimes I catch "ProgrammerException" to output a more specific error message. See, I have always a hierarchy of exceptions, first based on std::exception, then several others (ProgrammerException, EnvironmentException) and several based on that (AssertionFailedException, InvalidParameterException, InvalidPermissionsException, etc.).
I find this structure fairly readable, even if I return to a project months after I first wrote it.
Now, you'd like to completely remove main? That would only make the code less portable, as you'd have to write your own compiler or edit an open source compiler to load it. And just one single function in any file that does the same keeps it portable and readable.
You could just do what some libraries do and hide main() with a macro. Or just use Java.
It wouldn't require any compiler rewrites... After all the Linux kernel is written in C, has no main() function, and doesn't require a special compiler.
main() is just a part of standard C/C++. The C/C++ runtime calls it after initialization. With some adjustments to the runtime, some other function could be called (probably a static method of some class, like in Java). Not that I see any point in doing it.
A little off topic, but I suggest that you read:Quote:
Originally Posted by EVOEx
How Non-Member Functions Improve Encapsulation
GotW #84: Monoliths "Unstrung"
Designing Simple Interfaces
O.K., so for example:
And then instantiate like:Code:namespace xtd {
namespace impl_ {
struct application_base
{
application_base( void )
{
if( singleton )
throw;
singleton = this;
}
virtual int
entry_point( char**, char** ) = 0;
static application_base*
singleton;
};
application_base*
application_base::singleton = 0;
} // namespace impl_
template < typename Derived >
struct application : public impl_::application_base
{
template < typename Pointer >
Pointer
sentinal_end( Pointer ptr )
{
while( *ptr )
++ptr;
return ptr;
}
template < typename P1, typename P2, typename P3, typename A1, typename A2, typename A3 >
int
main_dispatcher( int ( Derived::* )( P1, P2, P3 ), A1 a1, A2 a2, A3 a3 )
{
return ( ( Derived* )this )->main( a1, a2, a3 );
}
template < typename P1, typename P2, typename A1, typename A2, typename A3 >
int
main_dispatcher( int ( Derived::* )( P1, P2 ), A1 a1, A2 a2, A3 a3 )
{
( ( Derived* )this )->main( a2, a3 );
}
template < typename P1, typename A1, typename A2, typename A3 >
int
main_dispatcher( int ( Derived::* )( P1 ), A1 a1, A2 a2, A3 a3 )
{
return ( ( Derived* )this )->main( a2 );
}
template < typename A1, typename A2, typename A3 >
int
main_dispatcher( int ( Derived::* )( void ), A1 a1, A2 a2, A3 a3 )
{
return ( ( Derived* )this )->main( );
}
int
entry_point( char** argv, char** envp )
{
typename Derived::strings
command_line( argv + 1, sentinal_end( argv + 1 ) ),
environment_variables( envp, sentinal_end( envp ) );
return main_dispatcher( &Derived::main, argv[ 0 ], command_line, environment_variables );
}
};
} // namespace xtd
int
main( int argc, char** argv, char** envp )
{
if( !xtd::impl_::application_base::singleton )
throw;
return xtd::impl_::application_base::singleton->entry_point( argv, envp );
}
Code:#include <iostream>
#include <vector>
using namespace std;
struct application : xtd::application< application >
{
typedef vector< string >
strings;
int
main( string const& name, strings const& commands, strings const& environment )
{
cout << "Application: " << name << endl;
if( !commands.empty( ) )
{
cout << "- Command Line -" << endl;
for( int i = 0, n = commands.size( ); i < n; i++ )
cout << i << ") " << commands[ i ] << endl;
}
if( !environment.empty( ) )
{
cout << "- Environment Variables -" << endl;
for( int i = 0, n = environment.size( ); i < n; i++ )
cout << i << ") " << environment[ i ] << endl;
}
}
}
instance;
Eeeewwwww!
What does this buy you? O_o
Soma
I have made similar before. I actually made one that had a lofily constructed entry point. All the pre-entry time code I wrote in assembler. I like how you include environment variables though, that is rather crafty of you. :)
So you want to turn C++ into Java?? Why?
Stand-alone functions have a useful purpose. Forcing people to put everything into a class, whether it needs to be there or not is just stupid.
This seems rather repetitious given the other replies, but, C++ is supposed to be a generic programming language. While main as the entry point is a holdover from C, I don't think that you could create something substantially different (like a System class or whatever you call it) and still claim C++ to be a generic programming language. Imagine how annoying it is to use functional decomposition to solve a particular problem and then have to instantiate some class to get it to work.
There's probably something you could do, but is that different from just using Java?
>> What does this buy you?
Good question. :) First of all, it makes support for multiple platforms more seamless. You could define a WinMain function, for instance, and a simple change of linker options would be all that would be needed to convert to a Win32 executable. Customization of the format of command-line and environment-variables makes processing program data more straightforward. Putting everything in a class effectively places all of the code into a private namespace, and simplifies the initialization and access of data that would normally be declared as global variables. Those are the most obvious benefits, but there are other aesthetic and practical reasons for doing things this way. It's interesting that most of the objections I've heard so far seem to be based on the fear of the unfamiliar or unorthodox. I have yet to hear a truly compelling reason why this sort of paradigm should *not* be used...
Well, you've already used 'argv[0]' which is basically guaranteed to be useless on all platforms; I'd say you are on the right track.Quote:
First of all, it makes support for multiple platforms more seamless.
The only difference between 'main' and 'WinMain' is the work and how much work is done before execution gets to the relevant function. You can have a single 'main' or a single 'WinMain' paired with more work in the middleware to handle both possibilities seamlessly.Quote:
You could define a [...] convert to a Win32 executable.
You've displaced a trivial amount of work, not solved CLI interface issues.Quote:
Customization of the [...] more straightforward.
Nothing you've done here would "normally be declared as global variables". But whether they might be or might not be doesn't really matter as all you've really done is wrap these normal global variables in a single global variable.Quote:
Putting everything in a [...] global variables.
Aesthetic are always personal, but I'd love to see a single practical reason for changing the start point of application execution. You've done, basically, what Microsoft did. I'm fine with displacing work, even trivial work, but that doesn't require this change.Quote:
Those are the most obvious benefits, but there are other aesthetic and practical reasons for doing things this way.
More than likely, you've heard valid, real-world concerns related to imposing designs or falsely licensing bad designs and simply interpreted them as you apparently have because you refuse to see the truth.Quote:
It's interesting [...] unfamiliar or unorthodox.
At the end of the day, you've provided the work for about two lines of code by costing interoperability with other existing libraries that think hiding 'main' is a good idea, imposing bad design by requiring an otherwise pointless class to be written, falsely licensing bad designs by implying that you've done more than hide global variables in 'this', and even imposing an additional global where none be needed--even with this sort of thing.
To be fair, all the other methods of hiding 'main' are just as bad--though they may be bad in different ways. Instead of doing work to hide 'main' behind lies why not craft something that makes writing 'main' easier? And just in case you say "I intend to provide more than two lines of code worth of work before I release anything.": you still shouldn't hide 'main'. There is little work in typing:
But even then, the other issues still stand.Code:int main(int argc, char ** argv)
{
return(my_app_class(argc, argv));
}
Soma
C++ is not supposed to be a generic programming language. It is not supposed to be a programming language with only one paradigm at all.
It is supposed to be a multi-paradigm language! Generic programming may be a big part and one of its strengths, but that does not make C++ a generic programming language...
Well, that's more or less what I mean, sorry. Did you disagree with the point, or only my choice of words?
I sounded as if you worded C++ a generic programming language only... I did disagree with that point, but if you actually meant what I just replied, then I do fully agree with you, of course. Just bad wording. Nothing serious.