Reusable source code

  • Thread starter James Dow Allen
  • Start date
J

James Dow Allen

Jacob's mention of his library reminds me of my own
suggestion to help with software reusability.

Much software has the form
High Level -- application-specific code
Mid Level -- application-specific code
Low Level -- FREQUENT FUNCTION, use library

But often one encounters
High Level -- application-specific code
Mid Level -- FREQUENT FUNCTION
Low Level -- application-specific code

A familiar example where this form is encountered and
dealt with is qsort(), with the low-level handled
simply with a function pointer.

If the qsort() source were recompiled with the
application-dependent comparison etc. it would speed
up, perhaps by 25%. The qsort.c source wouldn't
even change; instead the variation would be provided
by a very simple application-dependent header included
by qsort.c.

For example, make this definition:
#define COMP(x,y) \
(((struct elem *)x)->key - ((struct elem *)y)->key)
visible to qsort.c, rather than just pass a pointer
to the function
int ecomp(const void *x, const void *y)
{ return COMP(x,y); }

I mention this simple case to make the idea clear,
but if all I wanted to do is speed-up qsort() by 25%
I wouldn't be posting this. Instead I'm thinking
of cases where the common function CAN'T BE USED
AT ALL, without this flexibility.

In these interesting cases, the application-dependent
variations are much too complicated to be represented
with a few function arguments, but a common source
code could still be used, with any variations
coming from #define's in an application-dependent header.
(I have specific examples in mind, but won't mention
them. I want to focus on the idea of reusing
source code in the way I imply, rather than any
specific example.)

How about it? Anybody do this, or want to do it?
(There are examples of it *within* a buildable
library, but I'm talking about library source where
the user is encouraged to recompile a routine,
like qsort() but probably more complicated, using
his own application-specific header.)

James Dow Allen
 
S

Shao Miller

James said:
... ... ...
How about it? Anybody do this, or want to do it?
(There are examples of it *within* a buildable
library, but I'm talking about library source where
the user is encouraged to recompile a routine,
like qsort() but probably more complicated, using
his own application-specific header.)

I read your post twice but had a little trouble following it. That's my
problem so I'd like to ask for clarification, please: Are you asking if
there's a common way to override library routines with one's own code?
Are you asking if there ought to be a common way?
 
K

Kenny McCormack

You're trying to find my QUESTION. My post isn't
a question; it's a RECOMMENDATION.

I'm not asking HOW to do what I describe. That's easy.
I already do it in some of my own source code.

What I AM asking is: Why aren't the rest of you doing this?
:)

Hope this clarifies.
James

Those kinds of posts don't fly well in newsgroups like this.

Once you deviate from:

Student: Oh master, oh holy of holies, how do I do X? (And what is
wrong with the way a poor wretch like me is doing it?)

Master: You are subhuman slime, but I shall deign to answer thee.

the newsgroup falls off the rails. Sad, but true.
 
T

Tom St Denis

Those kinds of posts don't fly well in newsgroups like this.

Once you deviate from:

    Student: Oh master, oh holy of holies, how do I do X?  (And what is
    wrong with the way a poor wretch like me is doing it?)

    Master: You are subhuman slime, but I shall deign to answer thee.

the newsgroup falls off the rails.  Sad, but true.

usually when a newb comes into a group [any group] and says things
like "why not we do everything while standing on our heads?" it's
because they don't know what they're talking about.

This doesn't mean we don't listen to the newbs, it just means we don't
follow them. It's not our fault you can't tell the difference.

As to the specifics here, the solution to the OPs problem is called a
"processor cache" and "stack opcode optimizations" [something modern
high performance processors have] making the need to inline your
compare function rather moot.

Also, sometimes paying a small penalty in performance to make
something 100x generic/scalable is worth it in the end. I'd rather
have a standard generic qsort() like in the C lib than a 100 different
ones for all sorts of different data types.

And you can accomplish all sorts of "anonymous" code in C through the
use of structs with pointers to functions. I've written entire
libraries based on the concept. It allows me to construct very
modular/customizable applications out of standard C code without re-
inventing the wheel all the time.

Tom
 
S

Shao Miller

James said:
You're trying to find my QUESTION. My post isn't
a question; it's a RECOMMENDATION.

I'm not asking HOW to do what I describe. That's easy.
I already do it in some of my own source code.

What I AM asking is: Why aren't the rest of you doing this?
:)

Hope this clarifies.

Ok. Sorry for my misinterpretation. I was trying to understand:
... ... ...
How about it? Anybody do this, or want to do it?
(There are examples of it *within* a buildable
library, but I'm talking about library source where
the user is encouraged to recompile a routine,
like qsort() but probably more complicated, using
his own application-specific header.)

And mistook it to include two questions.

So I'd like to ask for a little bit more clarification: Are you
recommending a particular method for overriding library functions with
one's own code, via this header-inclusion strategy? Are you suggesting
that overriding library functions with one's own code is a pretty handy
thing to do, in general, to achieve performance benefits?
 
K

Kenny McCormack

Tom St Denis said:
usually when a newb comes into a group [any group] and says things
like "why not we do everything while standing on our heads?" it's
because they don't know what they're talking about.

Yup. James Dow Allen is a newb here. Never seen him post before, no
siree! He's got some nerve comin' in here and stirin' things up!

Thanks for proving my point.

--
But the Bush apologists hope that you won't remember all that. And they
also have a theory, which I've been hearing more and more - namely,
that President Obama, though not yet in office or even elected, caused the
2008 slump. You see, people were worried in advance about his future
policies, and that's what caused the economy to tank. Seriously.

(Paul Krugman - Addicted to Bush)
 
E

Eric Sosman

Jacob's mention of his library reminds me of my own
suggestion to help with software reusability.

Much software has the form
High Level -- application-specific code
Mid Level -- application-specific code
Low Level -- FREQUENT FUNCTION, use library

But often one encounters
High Level -- application-specific code
Mid Level -- FREQUENT FUNCTION
Low Level -- application-specific code

A familiar example where this form is encountered and
dealt with is qsort(), with the low-level handled
simply with a function pointer.

If the qsort() source were recompiled with the
application-dependent comparison etc. it would speed
up, perhaps by 25%.

Aside: Did you know that 84.6% of all stated percentages
are made up out of thin air?
The qsort.c source wouldn't
even change; instead the variation would be provided
by a very simple application-dependent header included
by qsort.c.

For example, make this definition:
#define COMP(x,y) \
(((struct elem *)x)->key - ((struct elem *)y)->key)
visible to qsort.c, rather than just pass a pointer
to the function
int ecomp(const void *x, const void *y)
{ return COMP(x,y); }

I mention this simple case to make the idea clear,
but if all I wanted to do is speed-up qsort() by 25%
I wouldn't be posting this. Instead I'm thinking
of cases where the common function CAN'T BE USED
AT ALL, without this flexibility.

A concrete example might be helpful here, despite
the misgivings you state below. In particular, I'd like
to understand the "CAN'T BE USED AT ALL" part.
In these interesting cases, the application-dependent
variations are much too complicated to be represented
with a few function arguments, but a common source
code could still be used, with any variations
coming from #define's in an application-dependent header.
(I have specific examples in mind, but won't mention
them. I want to focus on the idea of reusing
source code in the way I imply, rather than any
specific example.)

How about it? Anybody do this, or want to do it?
(There are examples of it *within* a buildable
library, but I'm talking about library source where
the user is encouraged to recompile a routine,
like qsort() but probably more complicated, using
his own application-specific header.)

It's done occasionally. I've seen a sort implementation
packaged as an #include file. You did something like

#define SORT_NAME mySort
#define SORT_TYPE struct frazzle
#define SORT_COMP(s,t) strcmp((s).key, (t).key)
#include "sort.h"

.... and got a `void mySort(struct frazzle *data, size_t nitems)'
function built for you. At a PPOE, a colleague put together a
serialization library that worked similarly: You #define'd a
bunch of stuff to describe the data structure you wanted to
serialize, then you invoked a macro from his header to generate
the actual code. So, variations on this theme are certainly
playable.

As to why they don't get more air time -- well, I'm sure
there are lots of reasons. Lack of direct support from the tools
that go along with the language has to be one of them: Lots of
debuggers, for example, have trouble stepping from line to line
through a function generated by a one-line macro invocation.
Mistakes by the client/user may well elicit compiler diagnostics
that are confusing at best, misleading at worst -- after the
#include or whatever you get a cascade of syntax errors and no
further clue. Testing becomes more laborious, because the innards
of the "module" are exposed to the surrounding code rather than
isolated from it, meaning that there are vastly more client/module
interactions to worry about.

My (vague) understanding of C++ templates is that they're a
kind of a way to regularize such an approach and give it some
help from the language itself and its tool set. But lacking such
support, the technique carries some disincentives that (it seems)
outweigh many of its advantages in the estimation of the coders.
 
B

BGB / cr88192

James Dow Allen said:
Jacob's mention of his library reminds me of my own
suggestion to help with software reusability.

Much software has the form
High Level -- application-specific code
Mid Level -- application-specific code
Low Level -- FREQUENT FUNCTION, use library

But often one encounters
High Level -- application-specific code
Mid Level -- FREQUENT FUNCTION
Low Level -- application-specific code

A familiar example where this form is encountered and
dealt with is qsort(), with the low-level handled
simply with a function pointer.

If the qsort() source were recompiled with the
application-dependent comparison etc. it would speed
up, perhaps by 25%. The qsort.c source wouldn't
even change; instead the variation would be provided
by a very simple application-dependent header included
by qsort.c.

it is unlikely to be that large.

rarely do most micro-optimizations really effect that much, which is part of
why optimizations in optimizing compilers make notable alterations to the
structure typically for only modest speedups in most cases.

For example, make this definition:
#define COMP(x,y) \
(((struct elem *)x)->key - ((struct elem *)y)->key)
visible to qsort.c, rather than just pass a pointer
to the function
int ecomp(const void *x, const void *y)
{ return COMP(x,y); }

I mention this simple case to make the idea clear,
but if all I wanted to do is speed-up qsort() by 25%
I wouldn't be posting this. Instead I'm thinking
of cases where the common function CAN'T BE USED
AT ALL, without this flexibility.

In these interesting cases, the application-dependent
variations are much too complicated to be represented
with a few function arguments, but a common source
code could still be used, with any variations
coming from #define's in an application-dependent header.
(I have specific examples in mind, but won't mention
them. I want to focus on the idea of reusing
source code in the way I imply, rather than any
specific example.)

How about it? Anybody do this, or want to do it?
(There are examples of it *within* a buildable
library, but I'm talking about library source where
the user is encouraged to recompile a routine,
like qsort() but probably more complicated, using
his own application-specific header.)

problems like this rarely come up, and usually are better solved via other
means (for example, vtable structures, ... are a common solution, ...).

in one case where a similar problem did pop up (though mostly due to a
poorly designed mass of code), I had basically ended up having to beat
together a somewhat modified C preprocessor to pull it off, as the stock C
preprocessor was too weak to really do these sorts of things...


but, as a general rule, likely the main reason things are not done this way
is because it is generally not very a good way to design ones' code...
(modularized decomposition of the problem space is typically a much cleaner
option, then followed by the use of vtables, ...).


or such...
 
I

Ian Collins

It's done occasionally. I've seen a sort implementation
packaged as an #include file. You did something like

#define SORT_NAME mySort
#define SORT_TYPE struct frazzle
#define SORT_COMP(s,t) strcmp((s).key, (t).key)
#include "sort.h"

.... and got a `void mySort(struct frazzle *data, size_t nitems)'
function built for you. At a PPOE, a colleague put together a
serialization library that worked similarly: You #define'd a
bunch of stuff to describe the data structure you wanted to
serialize, then you invoked a macro from his header to generate
the actual code. So, variations on this theme are certainly
playable.

As to why they don't get more air time -- well, I'm sure
there are lots of reasons. Lack of direct support from the tools
that go along with the language has to be one of them: Lots of
debuggers, for example, have trouble stepping from line to line
through a function generated by a one-line macro invocation.
Mistakes by the client/user may well elicit compiler diagnostics
that are confusing at best, misleading at worst -- after the
#include or whatever you get a cascade of syntax errors and no
further clue. Testing becomes more laborious, because the innards
of the "module" are exposed to the surrounding code rather than
isolated from it, meaning that there are vastly more client/module
interactions to worry about.

Based on my experience of the projects I've worked on in recent decades
macro trickery has gone out of favour. While a lot of these tracks can
be done with with the preprocessor, it's much cleaner (from a tool and
language support and maintenance perspective) to generate the code with
a code generator.
My (vague) understanding of C++ templates is that they're a
kind of a way to regularize such an approach and give it some
help from the language itself and its tool set. But lacking such
support, the technique carries some disincentives that (it seems)
outweigh many of its advantages in the estimation of the coders.

Indeed. It is much easier to do something the language supports!
 
J

James Dow Allen

I almost prefaced my message wtih a disclaimer
that SPEED WAS **NOT** MY CONCERN, but felt that a
simple speed-up case led to a clear example.
It seemed unnecessary -- I mention the point when
I mention speed.

But the only one to make a joke about the speed-up
was also the only one who grasped that speed
performance was not my primary concern!

     Aside: Did you know that 84.6% of all stated percentages
are made up out of thin air?

Aside: Did you suspect that, although any speedup was peripheral
to my main point, I actually did the implied experiment,
using glibc's qsort source, with constanted num, size,
compare? The element was
struct { int a, key, c, d; }
Speedup was 25%.
     A concrete example might be helpful here, despite
the misgivings you state below.  In particular, I'd like
to understand the "CAN'T BE USED AT ALL" part.

One example is hash-table management. If space-efficiency
is important, low-level details essential to the table
handling will vary, while higher-level functionality
(collision handling, resizing) may be invariant.
If this isn't clear, note that a space-efficient table
may pack key AND payload into 2 or 3 bytes, while
off-the-shelf hashtables often dictate 12 bytes PLUS key.

Hash-tables are the one example most clear to me.
(I posted the idea several years ago, but discussion
was diverted solely to hash-tables, with no discussion
of the reusable source code concept.)

Another place where the concept might work nicely
(though I've not tried it) would be alpha-beta
game-tree solving.
     It's done occasionally....

     As to why they don't get more air time -- well, I'm sure
there are lots of reasons.  Lack of direct support from the tools
that go along with the language has to be one of them: Lots of
debuggers, for example, have trouble stepping from line to line
through a function generated by a one-line macro invocation.
Mistakes by the client/user may well elicit compiler diagnostics
that are confusing at best, misleading at worst -- after the
#include or whatever you get a cascade of syntax errors and no
further clue.  Testing becomes more laborious, because the innards
of the "module" are exposed to the surrounding code rather than
isolated from it, meaning that there are vastly more client/module
interactions to worry about.

I'm assuming the user is an expert programmer.

Please note that there are two quite distinct approaches:

(1) common source is in header, often in the form of
#define'd code fragments, and invoked by application
via macros.
(2) common source is in .c file, application-specific
stuff is presented to .c via user-specific .h defines.
The .c is compiled (with its user-specific header) and
then behaves just as an ordinary .c/.o.

I'm thinking of (2) specifically, but Eric's comments
apply mostly to (1).

James Dow Allen
 
J

James Dow Allen

How would you specialise a function like qsort using 2?

In OP, I already presented the #define COMP I used,
which is the only non-trivial part of the answer to your question.
Obviously it would have a struct declaration in scope.

You would also want
#define QS_NAME SallySort /* or whatever */
And the .c would replace
void qsort_variant(...)
with
void QS_NAME(...)
so that you could use more than one instance of the
specialized qsort_variant() in a given executable.

But please remember: Speeding up qsort() is not my
major purpose. I used that as a simple well-known example.
My original purpose in the idea was to avoid rewriting
a hash-table manager even though all the low-level details changed.

James
 
B

BGB / cr88192

Francois Grieu said:
The generality of qsort comes at a high performance cost
in two areas
- the comparison thru a user-supplied function;
- the exchange.

The performance drop of library qsort versus a straight
equivalent specialized to the type sorted is usually way
*MORE* than 25%; I once tried and got a factor near 3
in a real case where I was sorting pointers to struct with
the sorting key a long, the first member of the struct.

Look at how your qsort performs the necessary exchange,
you'll probably be horrified by the complexity or/and
the lack of performance, unless qsort is defined inline
in <stdlib.h> (is that common ?), or the compiler/linker
is smarter than anything I own.

interesting, I wouldn't have thought it would have been that large...

although, admittedly, if I do custom sorts, often I am lazy and just do
bubble sort or selection sort, and usually only beat together a quicksort if
I feel performance may be an issue...
 
N

Nick

James Dow Allen said:
In OP, I already presented the #define COMP I used,
which is the only non-trivial part of the answer to your question.
Obviously it would have a struct declaration in scope.

You would also want
#define QS_NAME SallySort /* or whatever */
And the .c would replace
void qsort_variant(...)
with
void QS_NAME(...)
so that you could use more than one instance of the
specialized qsort_variant() in a given executable.

But please remember: Speeding up qsort() is not my
major purpose. I used that as a simple well-known example.
My original purpose in the idea was to avoid rewriting
a hash-table manager even though all the low-level details changed.

I can see problems with (2) [a supplied .c file, a user created .h file
that contains the specific stuff, to generate a suitable .o that you link in]
in those circumstances when you want more than one sort (or whatever) in
your program. You'll end up with sub-directories and fancy make files
(or whatever) as I can't see any way to have string_sort.c include
string_sort.h and integer_sort.c include integer_sort.h without hacking
on the .c files - in which case you don't need the .h files at all.

I can see times when you might want to do this, but I'm inclined to
agree with those who say that when you are getting to this complexity
it's often easy to move outside the C standard tools and generate code
in some other way.
 
M

Malcolm McLean

But often one encounters
  High Level -- application-specific code
    Mid Level -- FREQUENT FUNCTION
      Low Level -- application-specific code
Function pointers are one answer.

Another answer is "boilerplate" code. You do this every time you write
a new small program for Windows - you take the standard startup and
message pump routines, and then add in calls to your own program-
specific code.
 
J

James Dow Allen

You'll end up with sub-directories and fancy make files

sallysort.o: sallysort.c
rm qspecif.h
ln sallysort.h qspecif.h
cc -O -c -o sallysort.o qvariant.c
Much simpler than many makefiles.
I can see times when you might want to do this, but I'm inclined to
agree with those who say that when you are getting to this complexity
it's often easy to move outside the C standard tools and generate code
in some other way.

No. I've posted code to demonstrate the contrary.
Might post again if I get an indication anyone knows what
I'm talking about.

Hope this helps.
James
 
I

Ian Collins

In OP, I already presented the #define COMP I used,
which is the only non-trivial part of the answer to your question.
Obviously it would have a struct declaration in scope.

You would also want
#define QS_NAME SallySort /* or whatever */
And the .c would replace
void qsort_variant(...)
with
void QS_NAME(...)
so that you could use more than one instance of the
specialized qsort_variant() in a given executable.

I still think you are better off generating code another way. Certainly
if I find I need something repeated that's almost, but not quite the
same as something else I'll write some code to write the code. Although
I must admit I frequently use a code generator that emits macros to
generate code (test stubs)!
But please remember: Speeding up qsort() is not my
major purpose. I used that as a simple well-known example.
My original purpose in the idea was to avoid rewriting
a hash-table manager even though all the low-level details changed.

That sound like a great candidate for code generation.
 
J

James Dow Allen

I really should *not* post when I'm feeling
irritated. But ... whatever! :)

usually when a newb comes into a group [any group] and says things
like "why not we do everything while standing on our heads?" it's
because they don't know what they're talking about.

I don't want to brag about creeping senility, but
I've been a professional programmer since before many
or most of you were BORN. I've consulted for over a dozen
companies, written OS'es, the world's fastest Jpeg, etc.,
earned enough to retire early and happily. There's a good
chance that somewhere in your house right now there is a
chip whose hardware algorithm or firmware was designed by me.

But whatever.
As to the specifics here, the solution to the OPs problem is called a
"processor cache" and "stack opcode optimizations" [something modern
high performance processors have] making the need to inline your
compare function rather moot.

I specifically said speed performance was not my main
motive. Some should spend less time flaunting their
arrogance and actually reading the posts they respond to.

But whatever.

Incidentally, your comments about qsort's compare
performance display surprising ignorance.

But whatever.
Also, sometimes paying a small penalty in performance to make
something 100x generic/scalable is worth it in the end. I'd rather
have a standard generic qsort() like in the C lib than a 100 different
ones for all sorts of different data types.

I outlined an approach to "have your cake and eat it too"
on this *precise* issue -- except that source simplicity
and maintainability is my goal -- not speed.
Obviously I must have articulated it poorly. :)
Still, it strikes me as odd that with the specifics I
gave, a competent programmer couldn't understand the point.

But whatever.

I can see problems ... You'll end up with sub-directories and
fancy make files ...

If you actually need more than one instance of any of these
"reusable source code modules" in the same directory
(and this is relatively unlikely), then you can solve the
problem with a single line in the makefile:
ln -f sallysort.h qspecific.h

The result is a very simple system, with the "reusable
source module" eventually requiring ZERO further modifications.
All variations are encapsulated in the .h interface.

But whatever.

it is unlikely to be that large.

rarely do most micro-optimizations really effect that much, which is part of
why optimizations in optimizing compilers make notable alterations to the
structure typically for only modest speedups in most cases.

One needn't be a qsort() internals man to understand it spends
its time doing swaps (which will have either variable or
fixed total size depending on whether the suggestion is adopted)
and calling (*cmp)(). One doesn't have to be a compiler
expert to know that inlining doesn't affect pre-compiled
object files. One doesn't need to run a profiler to guess that
the described speed boost in qsort() will be significant. And
one doesn't have to be an algorithm complexity expert to know
that very many otherwise O(n) procedures are
dominated by O(n log n) sort.

*And especially*, one doesn't need expertise in English grammar to
read OP and see qsort() was chosen only as a simple example,
and that any question of speed performance was disclaimed.

But whatever.

Another answer is "boilerplate" code. You do this every time you write
a new small program for Windows - you take the standard startup and
message pump routines, and then add in calls to your own program-
specific code.

I added the otherwise-irrelevant "High-level application",
placing FREQUENT FUNCTION at mid-level *precisely* to avoid
this digression.

But whatever.

Thanks to all for the oh-so-careful attention! :)
James Dow Allen
 
N

Niklas Holsti

James said:
sallysort.o: sallysort.c
rm qspecif.h
ln sallysort.h qspecif.h
cc -O -c -o sallysort.o qvariant.c
Much simpler than many makefiles.

Well yes, but you won't be able to run a parallel make if there are
several instances of qvariant. I think it would be better to use an
instance-specific subdirectory, say sallysort/qspecif.h, and a -I option
on the "cc".

Alternatively, use a macro processor (perhaps even the C preprocessor)
to generate sallysort.c from qvariant.c and sallysort.h, then compile.

That said, I do think that James' suggestion can be useful to configure
and reuse C source code, or code in other languages (where it may be
necessary to separate the macro expansion step from the compilation step).
 
T

Tom St Denis

usually when a newb comes into a group [any group] and says things
like "why not we do everything while standing on our heads?" it's
because they don't know what they're talking about.

Yup.  James Dow Allen is a newb here.  Never seen him post before, no
siree!  He's got some nerve comin' in here and stirin' things up!

Thanks for proving my point.

If you had actually read what I wrote, I said we [most regulars]
**DO** read what they write. We just don't have to agree with it
because you say so.

That's how civil discussions work. Everyone gets a turn a the
microphone but at the end of the day we don't have to keep listening
[or follow whatever they say].

Tom
 
T

Tom St Denis

usually when a newb comes into a group [any group] and says things
like "why not we do everything while standing on our heads?" it's
because they don't know what they're talking about.

His suggestions didnt sound too nOObish to me.


This doesn't mean we don't listen to the newbs, it just means we don't
follow them.  It's not our fault you can't tell the difference.

Who is "we"?

I can see you've been left to run around establishing your "reg"
credentials for a little too long...

I've been an on-and-off poster for a long time.

What I don't get about you trolls is why you think there is a
hierarchy here. There isn't. I consider a few people here more
knowledgeable about the ISO C spec than myself but it's not like I pay
homage to them or something, or seek their approval or whatever.

All I was saying is out of a matter of respect we [general mob here]
tend to give new [and often incorrect] posters their due attention.
But doesn't mean we agree with it. So when someone comes around
trying to "radically alter our software paradigms" we listen, but if
they don't offer anything useful we file it away and don't act on it.

I for one, will keep developing against the standard C and POSIX.1
libraries since for industry they tend to work just fine. Even
though, for instance, I'm not going to use Navia's code doesn't mean I
think he shouldn't be able to post about it [note: I'm not judging the
quality of his project, I just don't want to use it is all].

The trolls here tend to think there is some sort of hierarchy and
unwritten rule, that if you're not a member of the club you get the
raw end of the deal. The reality is people who they think "aren't in
the club" then to be students/newbs coming around and asking
questions. They get schooled [more often than naught fairly politely
I might add] which then infuriates the trolls because how dare there
be disagreement!!!

You trolls really, really, really need to get a life. Look at what
you're doing. You're trolling "computers languages C" ... can you get
any nerdier and less useful than that? For all that is decent in this
world, seriously, go outside.

Tom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top