- Onwards and upwards - 1 Update
- Lapacke inputs - 1 Update
- lambdas as stateful function objects - 9 Updates
- Announce: Expressive C++, a readability/safety/convenience dialect of C++. - 5 Updates
- copy-on-write - 3 Updates
- boost::flyweight and smart pointers - 4 Updates
- A baffling bug in my bignumber code (expressive C++ dialect) - 1 Update
- Header-only C++, what is the point? In C this would be a obvious mistake - 1 Update
woodbrian77@gmail.com: Feb 26 11:32AM -0800 On Tuesday, January 3, 2017 at 9:11:27 PM UTC-6, Daniel wrote: > If I could make one suggestion, it would be to include some information in > the site's readme file about what this thing is and why anybody would want to > use it I wrote a Readme with several lines in it: https://github.com/Ebenezer-group/onwards/blob/master/example/Readme I'm going to take a documentation break for a while. Brian Ebenezer Enterprises http://webEbenezer.net |
thebtc@hotmail.com: Feb 26 10:07AM -0800 @Paavo- yes, you are totally right. I was being careless and wasn't typing the correct commands in. @Ben- I agree, your code is much more elegant than mine. I might go back and change things later, I'm not sure. I'm just a bit apprehensive because my prof for the course loves any possible reason to take off more marks... I'm back to the same problem as before- I'm getting rubbish being printed to my textfile. Here is my updated code: #include <iostream> #include <fstream> #include "lapacke.h" using namespace std; // allocate space for function LAPACKE_ssyev extern lapack_int LAPACKE_ssyev(int* matrix_layout, char* jobz, char* uplo, const lapack_int* n, float* a, const lapack_int* lda, float* w); int main(){ // list the declarations int matrix_layout = LAPACK_COL_MAJOR; // store arrays in column major char jobz = 'N'; // computes eigenvalues only char uplo = 'U'; // stores the upper triangle of a in an array a const lapack_int n = 10; // the order of the matrix a float a[n][n] = { {1, 11, 7, 9, 7, 11, 7, 9, 2, 11}, {11, 4, 10, 10, 6, 2, 9, 6, 10, 0}, {7, 10, 3, 5, 4, 4, 4, 4, 6, 10,}, {9, 10, 5, 3, 8, 8, 3, 5, 1, 8,}, {7, 6, 4, 8, 8, 10, 5, 6, 10, 0}, {11, 2, 4, 8, 10, 9, 4, 3, 5, 11}, {7, 9, 4, 3, 5, 4, 3, 10, 7, 2}, {9, 6, 4, 5, 6, 3, 10, 11, 1, 7}, {2, 10, 6, 1, 10, 5, 7, 1, 10, 5}, {11, 0, 10, 8, 0, 11, 2, 7, 5, 1} }; // the symmetric matrix a for which the eigenvalues will be found const lapack_int lda = n; // the leading dimension of the array a float w[n]; // stores the eigenvalues // open output file ofstream fout("eigenvaluesA.txt"); // compute the eigenvalues; function returns 0 if successful if (LAPACKE_ssyev(matrix_layout, jobz, uplo, n, &(a[0][0]), lda, w) == 0){ int i; for (i = 0; i < n; i++); // print the eigenvalues fout << w[i] << endl; }; // close file fout.close(); return 0; }; I'm not sure what the issue is with the printing. I also *have* to print my results to a textfile, that's the requirement. It seems to me I'm doing everything right so why isn't it printing? I'm getting "3.54082e-35" and that's it. |
Paul <pepstein5@gmail.com>: Feb 26 04:11AM -0800 The following code outputs 1 2 2. I wouldn't have expected this. 1 1 1 seems to make more sense. Also 1 2 3 would be my second guess. Apparently the explanation has to do with "stateful function objects" but I don't understand what this means. Can anyone explain? Thanks a lot. Paul int main() {int x = 0; auto foo = [=] () mutable { x++; return x; }; std::cout << foo() << " "; auto bar = foo; std::cout << foo() << " "; std::cout << bar() << " "; } |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 26 01:19PM +0100 On 26.02.2017 13:11, Paul wrote: > std::cout << foo() << " "; > std::cout << bar() << " "; > } First call of foo increments its x to 1 and returns that. Copying foo to bar copies the 1. Second call to foo increments its x to 2 and returns that. First call of bar increments its x to 2 and returns that. Not sure how you could think anything else could make sense? Cheers & hth., - Alf |
Paul <pepstein5@gmail.com>: Feb 26 04:39AM -0800 On Sunday, February 26, 2017 at 12:21:03 PM UTC, Alf P. Steinbach wrote: > Second call to foo increments its x to 2 and returns that. > First call of bar increments its x to 2 and returns that. > Not sure how you could think anything else could make sense? I still don't get it. In what sense does foo contain an x? Many people clearly think (erroneously) like me since this code features in a multiple choice quiz with several options. If the right output was obvious to everyone, no one would design such a quiz. For example, no one would write a c++ quiz question such as the below: int main() { std::cout << 2; } Which of these is printed by the above code? A) -3000000000 B) 2 C) Hello World D) 34134343434343 Paul |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 26 01:52PM +0100 On 26.02.2017 13:39, Paul wrote: >> First call of bar increments its x to 2 and returns that. >> Not sure how you could think anything else could make sense? > I still don't get it. In what sense does foo contain an x? In the sense of actually containing an x. You can rewrite the lambda as a class: class Foo_lambda { private: int x; public: auto operator()() -> int { x++; return x; } Foo_lambda( const int x_value ) : x{ x_value } {} }; The lambda expression produces an instance of this class, or of a class pretty much like it. That instance contains a data member called `x`, which due to the `=` capture by value, the constructor initializes with the value of the outer scope's `x`. If you had not declared the lambda `mutable` then the `operator()` would effectively have been `const` and couldn't have modified the data member. I write "effectively" because I don't know the details of the rules, and e.g. modification of `this` is prevented by treating it as an rvalue expression instead of treating it as `const`. But whatever the standard's formal mechanism is, `const` or rvalue or whatever, without C++17 `mutable` you're prevented from modifying. Cheers & hth., - Alf |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Feb 26 02:26PM On Sun, 26 Feb 2017 04:39:26 -0800 (PST) > I still don't get it. In what sense does foo contain an x? > Many people clearly think (erroneously) like me since this > code features in a multiple choice quiz with several options. I would be surprised if many people think like you. This is a standard lexical closure and most people will have come across them in python or javascript (which acquired them from the functional languages) if not via C++. You might want to look up closures on wikipedia. They can be confusing to begin with but they have become ubiquitous. In C++, lambda functions are a function object and the closure is effected via its capture list. In terms of implementation, in C++ the function object keeps the variables in its lexical environment which feature in the capture list as private members of the function object. You will find that C++ has its quirks, particularly about the way a lambda expression in a non-static member function captures a datum which is a member of the class - in effect, by default it takes it by reference, by capturing the 'this' pointer. For such a case, the default behaviour would result in you getting the "1 2 3" which was your second expectation, and _that_ is what normally surprises and confuses newcomers. |
Paavo Helde <myfirstname@osa.pri.ee>: Feb 26 04:28PM +0200 On 26.02.2017 14:11, Paul wrote: > int main() > {int x = 0; > auto foo = [=] () mutable { Here, you need to change [=] to [&] in order to get the 1 2 3 output. HTH. |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Feb 26 02:58PM On Sun, 26 Feb 2017 14:26:09 +0000 Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote: [snip] > default behaviour would result in you getting the "1 2 3" which was > your second expectation, and _that_ is what normally surprises and > confuses newcomers. It might be worth my making the additional point that you got "1 2 2" because in C++ objects are copied by value, and that includes function objects. This means that the captured variables held by the lambda function object are also copied by value when you copied the lambda. Languages in which objects (including functions) are copied by reference, such as javascript, will give "1 2 3". As another poster has already pointed out, you would get a similar effect in C++ if you captured the variables concerned by reference rather than by value. |
Ben Bacarisse <ben.usenet@bsb.me.uk>: Feb 26 03:23PM > javascript (which acquired them from the functional languages) if not > via C++. You might want to look up closures on wikipedia. They can be > confusing to begin with but they have become ubiquitous. I think it's a stretch to call this a standard lexical closure. Whilst the lambda part itself looks pretty standard., C++'s lambdas are peculiar in they can capture objects in the environment by copying, and these copies objectare a copied on assignment. That is perfectly natural in the context of C++, but you can't easily write that in the other languages you cite. > default behaviour would result in you getting the "1 2 3" which was your > second expectation, and _that_ is what normally surprises and confuses > newcomers. I have little experience anymore of what surprises newcomers, but 1 2 3 is what you get from writing the seemingly equivalent code in ECMAScript: var x = 0; var foo = function () { x++; return x; }; console.log(foo()); var bar = foo; console.log(foo()); console.log(bar()); If you try to emulate the "capture by copying" that [=] is doing in C++ you get 1 1 1: var x = 0; var foo = function () { var lx = x; lx++; return lx; }; console.log(foo()); var bar = foo; console.log(foo()); console.log(bar()); And now you are stuck because there is no obvious way to get the assignment of bar to copy the local variable lx 'inside' foo. Python is slightly less help as a model of what C++ is doing because you can't even write x += 1; return x in a lambda. You can write a function returning function but you might as well just name foo directly: x = 0; def foo(): x += 1; return x print(foo()) bar = foo print(foo()) print(bar()) This gives a run-time error because x is referenced before being assigned. You have to either state that you want the global one: x = 0; def foo(): global x; x += 1; return x print(foo()) bar = foo print(foo()) print(bar()) (giving 1 2 3) or you need to copy x and you go back to getting 1 1 1: x = 0; def foo(): lx = x; lx += 1; return lx print(foo()) bar = foo print(foo()) print(bar()) In short, experience of other languages with mutable state and function values will not help much in understanding the C++ code in the question. -- Ben. |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Feb 26 05:12PM On Sun, 26 Feb 2017 15:23:50 +0000 > copying, and these copies objectare a copied on assignment. That is > perfectly natural in the context of C++, but you can't easily write > that in the other languages you cite. I don't agree. A lexical closure is exactly what it is. The main distinguishing feature of C++ is that the free variables do not automatically close over the entire lexical environment: they must be specified in the capture list (which can of course be made to close over the entire environment with '='). Closures work by copying the captured variables, because they outlast the lifetime of the surrounding environment (and which is why overriding this by capturing free variables by reference in C++ is so dangerous). What is meant by "copy" depends on the language. Javascript for example copies simple numbers by value, and arrays, objects and functions by reference (strings are immutable so there is no difference), and most garbage collected languages do something similar. > In short, experience of other languages with mutable state and > function values will not help much in understanding the C++ code in > the question. The important issue for a learner to understand is how free variables in lambdas capture their lexical environment. After that, the differences in behavior when copying the lambda (and how often do you do that?) stem only from whether the lambda object, and any free variables, are copied by reference or by value; this difference being prompted in part by C++'s lifetime rules for automatic objects. Copy by reference of lambda function objects can trivially be implemented in C++ in the OP's code by changing 'auto bar = foo;' to 'auto& bar = foo;', which would then print "1 2 3". The reverse (copying functions by value) in javascript would require cloning the function object and all its captured variables. Java (as opposed to javascript) and python do not permit mutation of free variables at all. But all these different languages work, with C++, by lexical closure by free variables. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 26 01:07AM +0100 <url: https://github.com/alf-p-steinbach/Expressive-Cpp> Expressive C++ is a header only library. It implements a less unsafe, more convenient and readable-for-non-C++-expert-folks dialect of C++. To do this it depends on the following language extensions, which are supported by g++, Visual C++ and CLang, and probably also by far more compilers: • `__COUNTER__`, • `#pragma once`, • `#pragma push_macro`, • `#pragma pop_macro`, and • use of the `$` character in identifiers. The shortest possible Expressive C++ program is $just{} which is • shorter than a standard `main`, and • safer than a standard `main` ¹in the case of an exception being thrown out of it, and more • directly readable, without distracting bits such as the `int` in int main(){} And this is the general philosophy: ²not always shorter like here, but safer and to the non-expert more directly readable, and generally more convenient than the raw C++ that Expressive C++ code translates to. Flavor ³example: #include <p/expressive/use_weakly_all.hpp> #include <iostream> #include <vector> // std::vector #include <string> // std::string $use_weakly_all_from( std ); void cpp_main( ref_<const vector<string>> args ) { for( $each arg $in enumerated( args ) ) cout << "Arg " << arg.index() << " is '" << arg.object() << "'.\n"; } $start_with_ascii_arguments( cpp_main ) The `ref_` type builder, and others like it, allows one to use the principle of substitution to construct types, as in non-C languages in general. It also supports the practice of putting `const` first, even in nested parts. The resulting type specifications can be naturally read in the forward direction, unlike in raw C and C++. The $ words like $each and $in above, are pseudo keywords, keywords for the Expressive C++ dialect, implemented as macros. Expressive C++ also offers some stuff implemented with ordinary C++ code, using C++ core language features up to and including C++14. For example, the expression $invoked{ $var x=1; while( x*x < 50 ) ++x; return x - 1; } … uses an Expressive C++ pseudo keyword macro, `$invoked`, to produce a lambda, and to pass it to some ordinary C++14 machinery that invokes that lambda and produces its return value with the type implied by the return statement – which e.g., as here, allows a loop in an expression. Enjoy, even though this is just a version 0.3-or-something. - Alf Notes: ¹ If any statements are added, then unlike a raw `main` any exception out of the `$just` block is caught and reported on the standard error stream, with guaranteed stack rewinding/cleanup, and a suitable process exit code that adheres to the OS conventions is produced. ² I've not yet found a good way to express the `:?` operator within the limits of a macro approach. ³ Sorry about the limitation to "ascii" here. Implementing portable Unicode command line support, which raw C++ `main` doesn't offer for systems other than *nix, is much work, and I haven't got to that yet. |
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Feb 26 03:29AM On 26/02/2017 00:07, Alf P. Steinbach wrote: > ³ Sorry about the limitation to "ascii" here. Implementing portable > Unicode command line support, which raw C++ `main` doesn't offer for > systems other than *nix, is much work, and I haven't got to that yet. Use of dollar sign ($) in identifiers is implementation defined as to whether it works so is unportable and should be avoided. /Flibble |
Marcel Mueller <news.5.maazl@spamgourmet.org>: Feb 26 02:30PM +0100 On 26.02.17 01.07, Alf P. Steinbach wrote: > supported by g++, Visual C++ and CLang, and probably also by far more > compilers: • `__COUNTER__`, • `#pragma once`, • `#pragma push_macro`, • > `#pragma pop_macro`, and • use of the `$` character in identifiers. I did not get why making the code look as ugly as Perl/php/PowerShell sould be desirable. Readability and maintainability does not generally benefit from magic behind the scenes. Things may get even worse regarding compiler error messages. Marcel |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 26 02:52PM +0100 On 26.02.2017 14:30, Marcel Mueller wrote: >> compilers: • `__COUNTER__`, • `#pragma once`, • `#pragma push_macro`, • >> `#pragma pop_macro`, and • use of the `$` character in identifiers. > I did not get [it] [snip] I've started on tutorial-like documentation: <url: https://github.com/alf-p-steinbach/Expressive-Cpp/blob/master/README.md> I think reading that might help to start clear things up, at least when one keeps in mind that the rest of the library is similarly justified by very real problems with raw C++, especially for beginners. One needs to be aware of the shortcomings etc. that are addressed, before one can understand the advantages and trade-offs, and I hope that the docs I've started on do communicate this in a good way. :) Cheers & hth., - Alf |
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Feb 26 02:28PM On 26/02/2017 13:52, Alf P. Steinbach wrote: > One needs to be aware of the shortcomings etc. that are addressed, > before one can understand the advantages and trade-offs, and I hope that > the docs I've started on do communicate this in a good way. :) This library attempts to solve a problem that doesn't actually exist. At its best it is simply obfuscation and at its worst it is plain fucktardary. This library SERVES NO PURPOSE AT ALL. /Flibble |
"Chris M. Thomasson" <invalid@invalid.invalid>: Feb 25 07:46PM -0800 On 2/25/2017 6:38 AM, Öö Tiib wrote: >> which is the other part of that un-sharing-of-buffer operation. > How recently you have profiled that? The lock-free atomic thingies seem > more expensive than good old mutexes in multiprocessor systems in my tests. A lot of lock-free techniques are way to expensive, and require too many damn atomics and/or membars to get the job done. A typical generic mutex usually needs two atomic ops and an acquire/release membar paired relationship to keep the protected data visible. If a lock free technique needs more membars atomics than its mutex counterpart, then you are not going to really see a performance enhancement wrt mutex never hitting its slowpath, blocking. However, mutexs can block, and that can ruin their performance. Use locking patterns wisely; Deadlock no more... There are asymmetric locks, and non-blocking algorithms, but they are more exotic. ;^) Now, try to beat this sucker with a mutex based queue in a high pressure, loaded environment. Stress the queue out to see what happens. https://groups.google.com/d/topic/lock-free/acjQ3-89abE/discussion You can use the original CAS based version by Dmitry Vyukov. Or, my fast-pathed wait-free alteration: <pseudo code, membars aside for a moment> ______________________________________________ struct cell { uint32_t ver; double state; }; uint32_t head = 0; uint32_t tail = 0; cell cells[N]; // N must be a power of 2 void init() { for (uint32_t i = 0; i < N; ++i) cells[i].ver = i; } void producer(double state) { uint32_t ver = XADD(&head, 1); cell& c = cells[ver & (N - 1)]; while (LOAD(&c.ver) != ver) backoff(); c.state = state; STORE(&c.ver, ver + 1); } double consumer() { uint32_t ver = XADD(&tail, 1); cell& c = cells[ver & (N - 1)]; while (LOAD(&c.ver) != ver + 1) backoff(); double state = c.state; STORE(&c.ver, ver + N); return state; } ______________________________________________ Keep in mind, the tweak is wait-free on the atomics when fetch-and-add is hardware based, and has no loops, like most CAS and/or all LL/SC. Keep in mind that LOCK XADD is an example of a wait-free op, vs using CMPXCHG in a software based loop on Intel... Now, I also have a version of this with conditional waits here: https://groups.google.com/forum/#!original/lock-free/acjQ3-89abE/idSNj77HsIIJ |
JiiPee <no@notvalid.com>: Feb 26 10:15AM On 24/02/2017 23:28, Alf P. Steinbach wrote: > E.g. by simply not sharing mutable objects between threads. or doing mutex.... |
Marcel Mueller <news.5.maazl@spamgourmet.org>: Feb 26 11:57AM +0100 On 25.02.17 00.28, Alf P. Steinbach wrote: >> is not too good idea most of the time. > Uhm, thread safety is an orthogonal issue. Adding illusory thread safety > to a string class is going to cost, no matter the internal implementation. True. A work around is to make only volatile instances thread safe. And of course, this instances do not provide all operations. (Of course, this is some kind of abuse of the volatile keyword.) > threads, it would be impractical to lock down the object any time its > `operator[]` returns a reference that possibly could be stored by the > thread and used to modify the string at arbitrary times. There are some operations where thread safety makes no sense at all. Probably no one expects a single mutable string instance to be thread safe at any modifications. But when using CoW or deduplication another thread issues arises since now different string instances may share the same data. And of course, the programmer expects the instance methods of his own instance to require no global lock. In fact it is a good advice to separate mutable strings from immutable strings like Java or .NET do. (StringBuffer/Builder vs. String) The C++ string class unfortunately represents the mutable version. So introducing a immutable string class is a breaking change for many APIs. And no performance benefit arises when the efficient immutable class is converted to std::string all the time. However, in any larger program where I could control all the APIs I have done this job - with reasonable success. This concept is superior to CoW since the copy operations are now visible part of the type system. In practice this rarely happens at all since the use cases for mutable and immutable strings are quite different. > fine, I think. It's the responsibility of the code that uses those > classes, to ensure thread safe operation. E.g. by simply not sharing > mutable objects between threads. Ack. But this does not apply to objects that are shared internally like with CoW, since the programmer typically can't control this with reasonable effort. Marcel |
bitrex <bitrex@de.lete.earthlink.net>: Feb 25 06:38PM -0500 Say I have a class, call it Bitmap, which holds onto a texture resource, say "my_bitmap.png" which has to be allocated and deallocated through a constructor and destructor the "old-fashioned" way via new and delete. I then want to stick this class in a wrapper class that can be used as a key-value type for boost::flyweight, using a smart pointer to wrap the Bitmap class so that all objects in a graphics program that use "my_bitmap.png" can refer to the one instance, and when all objects that use it are destroyed the resource is release. Is it more appropriate to use a std::unique_ptr or a std::shared_ptr in the wrapper class in this circumstance? |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 26 01:09AM +0100 On 26.02.2017 00:38, bitrex wrote: > use it are destroyed the resource is release. > Is it more appropriate to use a std::unique_ptr or a std::shared_ptr in > the wrapper class in this circumstance? `std::shared_ptr` implements shared ownership, while `std::unique_ptr` implements single but transferable ownership. So ask yourself: do I have a case of shared ownership, or do I have a case of transferable single ownership? Or do I have something else, like e.g. automatic cloning on copy? Cheers & hth., - Alf |
bitrex <bitrex@de.lete.earthlink.net>: Feb 25 07:35PM -0500 On 02/25/2017 07:09 PM, Alf P. Steinbach wrote: > Or do I have something else, like e.g. automatic cloning on copy? > Cheers & hth., > - Alf Indeed, my point of confusion is about how boost::flyweight is operating internally. Instead of looking at the source right now (what a silly idea!) I'll instead just randomly speculate that the flyweight type is itself creating a wrapper type which holds a shared reference, and each time a new class is created with a key that hashes to the same value type it makes a new wrapper and clones the reference, rather than call the copy/move constructor on my own. So I'd guess that I'd want to use a unique_ptr for the resource in my wrapper, as at the end of the day all flyweights that hash to an instance of it are finally all referring to the same resource. When all the flyweights are gone the pointer to my wrapper is released, and along with it goes the unique_ptr contained within. |
woodbrian77@gmail.com: Feb 25 07:19PM -0800 On Saturday, February 25, 2017 at 6:36:35 PM UTC-6, bitrex wrote: > instance of it are finally all referring to the same resource. When all > the flyweights are gone the pointer to my wrapper is released, and along > with it goes the unique_ptr contained within. I would start with unique_ptr since it's simpler than shared_ptr. If you find later that shared_ptr would be helpful, you can switch to that. Brian Ebenezer Enterprises http://webEbenezer.net |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 26 01:50AM +0100 On 24.02.2017 21:08, Tim Rentsch wrote: > stuff. To look at any code more than about 10 or 20 > lines I would like to be able to compile it. If the > code is written in "expressive" C++ I can't do that. I've posted the current version of Expressive C++ on GitHub: <url: https://github.com/alf-p-steinbach/Expressive-Cpp> It uses a really tiny macro library, Macro Magic, that I also posted: <url: https://github.com/alf-p-steinbach/Macro-Magic/> Both libraries are header only and designed to be placed in a common `p` directory in the compiler's include path. `p` is my middle name shortened to one letter, and it's also short for "progrock". Not sure exactly where to take this but it fills my free time. And considering that you offered (if memory serves me!), I would be very grateful if you could take over the balanced tree tutorial. Another reason for that, if you have time, is that you evidently are familiar with the subject, knowing stuff that I didn't know. ;-) Cheers!, - Alf |
woodbrian77@gmail.com: Feb 25 03:13PM -0800 On Friday, February 24, 2017 at 2:19:15 PM UTC-6, Tim Rentsch wrote: > otherwise. From the author's point of view, the page being > posted is pretty much all free advertsing, with negligible > downside. Authors and publishers often give free access to a few pages of their books. An individual giving away additional pages is making a higher percentage of the book available for free. Some people won't buy the cow when they can get the milk for free. http://nypost.com/2017/02/24/150k-supercar-stolen-during-test-drive/ At some point, the higher percentage that's free the less likely people are to pay for it. I'm thankful I don't need to make a living as a traditional author. Brian Ebenezer Enterprises - In G-d we trust. http://webEbenezer.net |
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
No comments:
Post a Comment