
From: christopher diggins <cdiggins@videotron.ca>
From: "Jonathan Turkanis" <technews@kangaroologic.com>
void DoWork() { // do work }
int main() { DoWork(); }
Okay, but do you really expect people to start writing programs this way?
Yes, especially if the iostreams library provides the functionality to them. My point is that C++ code is pointlessly hard to reuse as is, and I am pushing for new ways to make small programs more reusable. This is incrediby important when managing large numbers of small programs (for instance library tests and demos). It is trivial to refactor code to make it look like the above, just cut and paste the main!
You assume that nothing fails in the above, since main() falls through and, therefore, returns zero. Are you proposing that main() should actually catch exceptions and, possibly, extract its exit status from the exception object?
I think people would only do this to conform to your filter concept. My question is: why aren't the other concepts sufficient?
The other concepts are fine, they are just more obfuscated than most programmers require. Just imagine trying to explain how to use a filter
You've admitted to being relatively new to C++. Can you rightly determine what "most programmers require?"
concept in a way which makes sense to a Java / Delphi / C programmer. I think it is important to try and provide alternatives where possible which makes sense to professional programmers who may not be familiar with the intricacies of generic programming techniques and functors.
A reasonable goal, though all C++ programmers need to become familiar with generic programming and function objects. These really aren't novel or academic techniques.
I guess I should have asked for a *realistic* example. If you really write such simple programs you don't have to worry about reuse; it's simpler to write the whole program again from scratch.
First off I do write programs as simple as that and I have a lot of them.
In the *nix tradition, such simple filters are exactly what's desirable, at least for assembly into pipelines.
This occurs frequently for testing, prototypes, demos, and systems admin. I strongly disagree with maintaining multiple code bases, rather than refactoring and reusing the code. As a professional coder I am always looking for ways to be more productive and and have less code to manage.
I think Jonathan meant that a program as simple as your toupper example would more likely be written as a library function, to be reused in other programs, than as a standalone program.
Nonetheless, I do currently have a non-trivial program which converts C++ into a <pre></pre> html tag, CppToHtmlPreTag, it operates obviously on the stdin and outputs to stdout. It looks essentially like this:
void CppToHtmlPreTag() { // calls multiple other functions to do the work };
int main() { CppToHtmlPreTag(); return 0; }
So the result of this program is to write "<PRE>" to stdout, copy stdin to stdout, and write "</PRE>"? Why do you need a program for that? I can see needing a general purpose program for copying stdin to stdout such that a script can print/echo "<PRE>", call the no-op filter, and then print/echo "</PRE>" as well as other variations. You could even write a general purpose program that took two arguments -- strings -- that it writes before and after copying stdin to stdout. On *nix, that "general purpose program for copying stdin to stdout" could be awk: #!/bin/sh echo "<PRE>" awk '{print}' echo "</PRE>" A "general purpose program that took two arguments -- strings -- that it writes before and after copying stdin to stdout" can be, on *nix: #!/bin/sh if test -n "$1"; then echo "$1" fi awk '{print}' if test -n "$2"; then echo "$2" fi The point is that there are several approaches to your simple goal that don't involve something as complex as you've built, and yet provides all of the benefits. Another approach is something like Microsoft's rundll32.exe which loads a DLL, locates a named entry point, and then passes some arguments to it. If the function signature requirements are a problem, you could create your own version. Assembling your chain would involve a batch/command file that calls rundll32.exe (or your tool) as many times as needed using the shell's I/O redirection to assemble the pieces. IOW, I don't think I see the value in rewriting existing code to conform to your filter's interface such that it can be assembled into a pipeline via C++ when pipelining is a forte of the shell (at least *nix shells).
I want to reuse this program in another program which outputs an entire Html Docucment with a header and footer. ( CppToHtmlDoc ). The easiest way I can think of to do this is to write a new program such as (this is to a certain degree psuedo-code):
struct CppToHtmlDoc { CppToHtmlDoc(string css, string title) : mCss(css), mTitle(title); void filter() { cout << "<html><head><title>" << mTitle; cout << "</title><link rel='stylesheet' type='text/css' href='"; cout << mCss << "'/><body>" cin | CppToHtmlPreTag(); cout << "</body></html>"; } string mCss; string mTitle }
int main(int argc, char** argv) { assert(argc == 4); CppToHtmlDoc(argv[1], argv[2]) | filestream(argv[3]); }
So I wrote program2 using the [b] approach you outlined which I agree that it is superior for this program. I also managed to retain my original code precisely as is using the [a] approach. If I had to rewrite the original program to use a filter concept I would have had to rewrite *all* of my functions to pass the the Source and Sink types to each one, and to use src and snk instead of cin / cout.
This would be even easier to assemble via scripting and you don't need to compile anything or maintain source and binaries independently.
I guess my point here is that I am able to refactor existing code more easily and quickly if you support [a] and [b] syntax. [c] is perfectly acceptable, and has its advantages in several scenarios, even though it is overkill for my work.
That argument is even stronger for using scripting to assemble such building blocks.
I just want to be able to write:
filter1() | filter2();
as a statement, with the implicit understanding it pumps from cin and to cout.
You can do that if "filter1" and "filter2" are filter applications: #!/bin/sh filter1 | filter2 With that, you didn't have to make any changes to filter1 or filter2 to be able to form a pipeline. That's even simpler! -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;