This discussion about Classes in Clarion took place on Skype recently and I thought I would try posting it here to retain because it had a lot of info. I think I got it all. I did a few search-and-replace to try to make it more readable.
This is a WIKI so anyone can edit, but I would suggest only the participants edit, others should post replies.
Mark Goldberg, 2:11 PM
1 template can export any file
So since you said --^
it sounds like youâre using property syntax
but then⌠why use code generation?
Why not just put this inside of a class ?
PeterPetropoulos, 2:13 PM
i still donât quite get classes. youâve shown me how to do some simple ones, but i donât see the difference.
Mark Goldberg, 2:13 PM
INCLUDE('ctFileExporter.inc'),ONCE
FileExporter ctFileExporter
CODE
FileExporter.FileName('yada.csv')
FileExporter.Export(MyTable)
PeterPetropoulos, 2:14 PM
and itâs generally multiple files, multiple fields
Mark Goldberg, 2:15 PM
the advantage to template code is
it can discover code once, and compile that in
also you have access to more details of the dictionary
In general, if youâre discovering stuff at runtime
then you could take the same code youâre generating
write it once in a class and just use it
PeterPetropoulos, 2:19 PM
iâm not getting it, that is, i donât see the difference. the procedure still has to create and open the output file, reporting back errors, gather data from multiple sources, writing and reporting back errors, the code all exists somewhere. what is the difference between a procedure generated in template code and one in class code?
i want to understand the difference, but i donât see the difference. i see the concept, but i donât get how to differentiate
Mark Goldberg, 2:23 PM
So I mentioned that advantage of template code above
IMO The disadvantages are
-
it slows down process to create and compile code
which is to say, using the template -
maintenance is harder
youâre working in dual languages when writing a template
the template language itself
and the code to be generated -
maintenance is slower
to make a change to template
you write the change
(i donât recall, but I donât think you need to re-register
or otherwise inform the IDE that the template has changed )
regenerate the code
compile the codenote a regeneration might unnecessarily touch global things
thereby triggering a much longer compile phase
PeterPetropoulos, 2:25 PM
template changes re-register automatically
Mark Goldberg, 2:25 PM
So, if the resulting code could be wrapped up in a class
and used very simply (see my code example above)
then⌠why not just do that ⌠itâs faster
PeterPetropoulos, 2:26 PM
but thereâs still a bunch of code that backs up the few lines you showed above
Mark Goldberg, 2:26 PM
yes there is
just like the template
write it once
call it as many times as you need
in some ways the resulting code is easier to work with too
as the technical details of how to do the .Export(File)
are out of your way
theyâve been delegated to that class
and you can keep thinking on a higher level (abstraction)
[Carl: Class code can be Tested / Exercised using Clarion Test, not possible with TPL]
PeterPetropoulos, 2:27 PM
I write the template once, use it as many times as I need. still not seeing the difference
Mark Goldberg, 2:28 PM
all you see, are the import bits, the filename, the file label
not all of the technical crap needed to actually do the work
Also if you are doing multiple exports in a single program
then the code generation is code bloat
(unless you generate a procedure, and call that)
But then⌠thatâs really pretty similar to the class
so why waste the code generation time
generating the same code over and over and over and over again
PeterPetropoulos, 2:30 PM
If the INC file needs to be placed in any export program, how is it not code bloat?
[Carl: I would say the .INC file is not compiled in and bloat, it is loaded by the compiler into memory so it has a list of Procedure signatures to validate against else you get Undefined Procedure.]
Mark Goldberg, 2:31 PM
Itâs not bloat
because itâs only in the program once
remember I said if you are doing MULTIPLE exports in a single program
PeterPetropoulos, 2:32 PM
If you have 10 export programs, they all access the single INC file?
Mark Goldberg, 2:32 PM
Yes
PeterPetropoulos, 2:32 PM
The INC file does not exist multiple times?
Mark Goldberg, 2:32 PM
correct: INCLUDE('ctFileExporter.inc'),ONCE
PeterPetropoulos, 2:33 PM
where is the include file defined? global data of the app?
Mark Goldberg, 2:33 PM
For any given scope the INC is included once
which results in the MOUDLE being compiled in
An .INC of a class with a MODULE() attributes needs to be at a static scope
this is typically done at a global scope
but it is allowed a module scope
PeterPetropoulos, 2:34 PM
the module part has always eluded me
Mark Goldberg, 2:34 PM
which module part is that ?
PeterPetropoulos, 2:35 PM
any of it
Mark Goldberg, 2:35 PM
PeterPetropoulos, 2:36 PM
program, procedure, module, routine. i donât understand where module is in the hierarchy (if thatâs the right word)
Mark Goldberg, 2:36 PM
ok, so a class can have ,MODULE('Yada.clw')
when the compiler encounters this
it looks for the code for the methods in that module
a class can also have ,LINK('yada.clw')
this causes the compiler to compile that module and link it in
So you donât have a explicitly add it to your .cwproj (project in the solution explorer)
Mark Goldberg, 2:40 PM
program, procedure, module, routine. i donât understand where module is in the hierarchy (if thatâs the right word)
PeterPetropoulos, Yesterday at 2:36 PM
So in terms of SCOPE
We have
Program (which is often called GLOBAL scope)
Module a given .CLW
you can have data that appears at the top of the file
before the first PROCEDURE
PROCEDURE
data (kept on the stack)
ROUTINES - the smallest scope
can have a DATA/CODE section
data cannot be shared from a routine to higher scope
(except for implicit variables - which IMO should be avoided in general)
Artigas, 2:40 PM
That template is 18+ years old. However, it is useful as a learning tool. And it also generates templates.
Mark Goldberg, 2:40 PM
Now there are some other level of scope
having to do with procedures or methods
inside of procedures or methods
PeterPetropoulos, 2:41 PM
so module exists âaboveâ procedures so that multiple procedures can link to it
Mark Goldberg, 2:42 PM
every procedure belongs to exactly one module
module data can only be seen by procedures in the same module
PeterPetropoulos, 2:43 PM
every procedure belongs to exactly one module
Mark Goldberg, Yesterday at 2:42 PM
seems contradictory to me
Mark Goldberg, 2:43 PM
OK, so
Open some source code to look at a procedure
PeterPetropoulos, 2:44 PM
oh, wait, that doesnât mean module : procedure is 1:1
Mark Goldberg, 2:44 PM
no, many procedures can be in the same module
PeterPetropoulos, 2:45 PM
ouch, thumb in eye
Mark Goldberg, 2:48 PM
So, letâs look at ⌠Libsrc\Win\BRWExt.clw
PeterPetropoulos, 2:48 PM
i am looking at the generated source and seeing it differently now
Mark Goldberg, 2:48 PM
oh good
PeterPetropoulos, 2:49 PM
got the brw
Mark Goldberg, 2:49 PM
So I was going to point out that BRWExt.clw
has MODULE scoped data
for example cls_Counter LONG(0),THREAD
Mark Goldberg, 2:50 PM
The module is BRWExt.clw
there is some data at the top of the file before the first procedure
the first procedure (ok method) we see in the file is ListFormatManagerClass.Construct
Mark Goldberg, 2:51 PM
but as you can see there are more procedures (methods) in the module than just that one
Mark Goldberg, 2:51 PM
For instance we also see ListFormatManagerClass.Destruct
Mark Goldberg, 2:52 PM
now, each of those two procedure belong to the same module
and only that one module
(theyâre not in ABFILE.clw too)
Only procedures inside of the module can directly access the module scoped data
ex: the CLS_Counter
code that exists outside of the module, cannot see that variable
itâs âout of scopeâ
PeterPetropoulos, 2:53 PM
ok encapsulation
Mark Goldberg, 2:54 PM
yeah, thatâs fair to say
PeterPetropoulos, 2:55 PM
i took the classes, but not enough examples. lots of generalized talk about pies (taken from topspeed docs), no examples
Mark Goldberg, 2:56 PM
FWIW all of the conversation about scope
is about CW as a language in general
itâs not limited to CLASSes
PeterPetropoulos, 2:56 PM
i get scope
not just the blue kind, either
Mark Goldberg, 2:57 PM
There is one more nuance I havenât mentioned
and thatâs the MEMBER statement
which is the first thing that appears in a module
there are two forms
MEMBER or MEMBER()
and MEMBER(âMyGlobalModuleâ)
my term for the first are âempty membersâ
a module that has an empty member statement
HAS NO GLOBAL SCOPE THAT IT CAN SEE
none
zip
nada
Mark Goldberg, 2:59 PM
But whatâs really handy about empty member code
is that it can be compiled into any program
without being altered
Itâs fairly common to see CLASSes use empty member modules
That said, I certainly have written classes for my program
that have a member(âmyProgâ) statement
heck I even have a few, with conditional omit/compiles around the member statement
so I can compile it into my main program and also into a utility program
that works with the same âdictionaryâ
OMIT('**',bw4utils)
MEMBER('B4.clw')
!END-OMIT('**',bw4utils)
COMPILE('**',bw4utils)
MEMBER('BW4Utils.clw')
!END-COMPILE('**',bw4utils)
MAP
END
Mark Goldberg, 3:03 PM
any questions about scope ?
PeterPetropoulos, 3:04 PM
not now, but the point is that classes can be local or global, depending on the need. isnât that the point? the difference I asked about?
Mark Goldberg, 3:05 PM
When talking about scope
think of an instance of a class as DATA
just a like a LONG
PeterPetropoulos, 3:05 PM
DATA, even tho thereâs executable code in it
Mark Goldberg, 3:05 PM
a LONG variable can exist at all different kinds of scope
an instance of a class can exist as the same different kinds of scope
PeterPetropoulos, 3:06 PM
the same different, not getting it
Mark Goldberg, 3:06 PM
See a CLASS is TWO things
Itâs a GROUP of data
and itâs a collection of procedures (known as methods)
PeterPetropoulos, 3:06 PM
methods
Mark Goldberg, 3:07 PM
that bot is getting old
OK, so just think about LONGs for a moment
MyCounter LONG
that can appear
Global
Module
Procedure
Routine
Mark Goldberg, 3:08 PM
Now, letâs thing about
MyClass ctClass
it too can appear
Global
Module
Procedure
Routine
Mark Goldberg, 3:08 PM
MyClass is just a GROUP
and where we declare that class group
controls itâs scope
when I say MyClass is just a group
I do get that there are usually methods declared with the class
but thatâs a detail I donât want to have you focus on at the moment
Mark Goldberg, 3:12 PM
are you with me on the DATA side of a class
Being really just a group
and a group is declared at a given scope
PeterPetropoulos, 3:13 PM
itâs a group that will have fields, right?
PeterPetropoulos, 3:13 PM
it may contain TYPEs, or just fields, and theyâre all gathered under the scope, the âumbrellaâ of the group
Mark Goldberg, 3:14 PM
a group cannot contain types
It can contain references - if thatâs what you mean
PeterPetropoulos, 3:14 PM
that rings a bell. ok
Mark Goldberg, 3:15 PM
FWIW, you can literally derive a class from a group
OK, Classes are TWO things
Data (known as Properties)
Code (known as methods)
PeterPetropoulos, 3:16 PM
right
Mark Goldberg, 3:16 PM
Now, say the compiler didnât support classes
PeterPetropoulos, 3:16 PM
oh donât say that
Mark Goldberg, 3:16 PM
(note: what Iâm about to describe, would not support PARENT or Virtual methods)
So if you were working in a language that didnât support classes
but you could pass a group
this is what youâd do
Say I have the following
Yada CLASS,TYPE
FieldA LONG
FieldB LONG
Sum PROCEURE(),LONG
Mult PROCEDURE(),LONG
END
Weâd write something like
Yada.Sum PROCEDURE()
CODE
RETURN SELF.FieldA + SELF.FieldB
PeterPetropoulos, 3:19 PM
right
Mark Goldberg, 3:20 PM
No, say we donât have classesâŚ
Weâd write
gtYada GROUP,TYPE
FieldA LONG
FieldB LONG
END
Sum PROCEDURE(*gtYada xSELF)
CODE
RETURN xSelf.FieldA + xSelf.FieldB
Mark Goldberg, 3:21 PM
well, it turns outâŚ
THATâS WHAT THE COMPILER IS DOING
you can LITERALLY write
Yada.Sum()
or
Sum( Yada )
You might have heard that when using OMITTED( 2 )
where 2 is any number
that when youâre working with a METHOD
that you need to add one
as SELF is a hidden first argument
You might have noticed the compiler showing name mangled method names
and if you look closely at those, the compiler is literally passing the classâs group of data
as the first argument
PeterPetropoulos, 3:24 PM
did not notice, but ok
Mark Goldberg, 3:24 PM
NowâŚ
Say you wrote your code that way
youâd have ONE Sum procedure in your code
and sprinkled throughout your code
you might have various instances of the gtYada groups
There is no code bloat
there is only ONE Sum procedure
But itâs called different times, with different arguments
So thatâs the CORE concept of classes
Yes, there are some important other features
like VIRTUAL methods
and CONSTRUCT/DESTRUCT
But⌠the core is the same as a GROUPs of data and procedures that take that group as a first argument
PeterPetropoulos, 3:27 PM
iâm using groups, types, and ordinary procedural data. i understand what youâre saying
Mark Goldberg, 3:28 PM
So⌠circling back to early in this conversation
If you have code that just takes a FILE parameter
then you could put that code into a procedure (or into a method)
can call it
vs. generating the same code in many places
PeterPetropoulos, 3:29 PM
so, youâre saying that by using a class, the data has multiple instances, the code only 1?
Mark Goldberg, 3:29 PM
Exactly
PeterPetropoulos, 3:33 PM
i have been working around that problem by creating multi-procedure apps (each doing a specific group of tasks), linking them to the caller apps, all apps having the same data definitions.
Mark Goldberg, 3:35 PM
So classes have a built in mechanism to help with that.
In both the DLL and the EXE you INLCUDE(âctYada.incâ),ONCE
however the LINK attribute of the class (and the DLL attribute too)
vary using pragmaâs
So by INCLUDE()
ing you get the same data (type) and method declarations
and by setting it so that the it only compiles into one
and expects the address of the code to be pointed to via a LIB for the others
PeterPetropoulos, 3:39 PM
so, assuming the dll contains multiple procedures for export, the class (data and code) are included at the global level, with links in the user procedures to access the code
Mark Goldberg, 3:40 PM
I want to be certain itâs clear.
PeterPetropoulos, 3:40 PM
thatâs why iâm trying to repeat it back
Mark Goldberg, 3:40 PM
I know
In the DLL you have
INCLUDE('ctYada.inc'),ONCE
and you have the pragma set to to compile the code
the .EXP will have to be revised to mention a number of things
each method, and a few other things
Templates can alter the EXP for you
The compiler will create a .LIB for the DLL
and the .LIB will have the address of each thing mentioned in the .EXP
Now, the DLL MIGHT or might NOT have any instances of the class
an instance of the class, is like an instance of any GROUP
In the EXE You also write
INCLUDE(âctYada.incâ),ONCE
but this time the pragmaâs are set, to NOT compile
rather we expect to find the address of methods (etc) in the .LIB
Again we can have instances of the class (data) here (or not)
PeterPetropoulos, 3:46 PM
why are we talking about the EXE? i think of the exe as a frame that calls procedures from dlls
Mark Goldberg, 3:46 PM
Well, you brought up DLLs
I thought thatâs what you were thinking about
PeterPetropoulos, 3:47 PM
the exe doesnât know anything about the âworkerâ dlls
the caller and worker dlls know each other, but the exe doesnât need to know about that
Mark Goldberg, 3:47 PM
OK, so you can still have a dependency
Where you have
DLL_X
which is dependent on DLL_Y
Mark Goldberg, 3:49 PM
So for code that is shared by DLL_M and DLL_N
you might create DLL_X
which contains the compiled FileExport class (and maybe other things)
and then DLL_M and DLL_N link in DLL_X
PeterPetropoulos, 3:50 PM
right
but youâre describing how I do it, not the way it should be done, right?
wait
let me ask it this way
did you describe the proper way to do this in a class, or are you reiterating how i have been doing it?
Mark Goldberg, 3:51 PM
Iâm saying Classes can be used that way
and the LINK/DLL attributes set via pragmas
make it easier to keep code consistent
PeterPetropoulos, 3:51 PM
what abc calls the âCallâ list?
Mark Goldberg, 3:51 PM
Possibly - I have no idea
PeterPetropoulos, 3:52 PM
Mark Goldberg, 3:52 PM
I havenât heard of the Call list
PeterPetropoulos, 3:52 PM
in the IDE, highlight a procedure, click the âCallâ button and see a list of procedures
Mark Goldberg, 3:52 PM
thatâs not ABC, thatâs just the IDE for AppGen
PeterPetropoulos, 3:52 PM
select the one(s) that apply
sorry
so, in my own backward way, Iâve made a class or 2?
Mark Goldberg, 3:53 PM
IIRC, thatâs mainly there to add things youâre calling via embeds
so that they show up in the tree
PeterPetropoulos, 3:53 PM
right, but it allows one to call procedures linked in from other dlls
Mark Goldberg, 3:53 PM
ok
So can you use that to make a method call ?
Iâm unclear about AppGen limitations
PeterPetropoulos, 3:55 PM
yes. the called dll has a âgatewayâ procedure that receives a GROUP, with an action that tells it what procedure(s) to call
Mark Goldberg, 3:56 PM
that said, you can literally call methods passing the class instance as the first parameter
so Iâd think you would be able to, even if the IDE is dain bramaged
PeterPetropoulos, 3:56 PM
the calling dll has one or more small procedures that i call to set the GROUP and call the âgatewayâ procedure
thereâs a lot of that going around
but as i understand what youâre saying, classes generally sit in separate dlls that get linked to caller dlls. the inc file contains all of the methods, so that the caller can address them directly, and a âgatewayâ procedure is not needed
Mark Goldberg, 3:59 PM
Classes can appear anywhere
So I wouldnât say they âgenerally sitâ⌠anywhere
PeterPetropoulos, 4:00 PM
ok
but a large, complicated class would probably exist in its own dll?
Mark Goldberg, 4:00 PM
Itâs a design choice
PeterPetropoulos, 4:00 PM
so, it could be hand code in the dllâs global data
Mark Goldberg, 4:02 PM
I have some beasts of classes
which when you follow them all of the way down
they probably have 100 classes that they are dependent on
and it all adds up the 250,000 lines of code
(rough numbers off the top of my head)
I just leave them in my main EXE
now, should I move them to a DLL ?
I could and that would speed up my recompile time
but my time is already pretty quick
PeterPetropoulos, 4:03 PM
i am accustomed to a small exe and big dlls. you have a really big exe?
Mark Goldberg, 4:04 PM
I have an EXE and itâs not that big, I avoid code bloat
as I think about it, 250,000 is probably really really LOW itâs likely 3-4 times that
my EXE is roughly 650K, while I have some DLLs
They are not written in clarion