Skype Chat on Clarion Classes with Mark Goldberg reply to "I still don't quite get classes"

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.

:globe_with_meridians: :globe_with_meridians::globe_with_meridians::globe_with_meridians::globe_with_meridians::globe_with_meridians::globe_with_meridians::globe_with_meridians::globe_with_meridians::globe_with_meridians:

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 code

    note 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

:slight_smile:

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

4 Likes