Classes Help required to call Function defined in APP

Hello ,

After the knowledge I obtained from my previous question about (Classes tutorial), I started recreating new classes from my own templates which I already created before to cover the functions side.

Today I faced a problem that one of the procedures I defined in the class uses a function created in one of my templates, although the template is added to the application and the function is working fine in the application, after including the class I am getting an error “Unknown function label” although I gave a unique name for the procedure in the class.(the new function in the class is a new one and not found in the templates)

Regards

You either need to declare your function at a global level (Use the global MAP) or add the prototype to the local procedures MAP

Sorry but can you elaborate more

Regards

I suspect your class member is generic. IOW, it’s MEMBER statement at the top doesn’t name your program (and it shouldn’t).

In this situation, I suggest you create an empty virtual method in your class. Then generate a global derived class in your application, which also derives that virtual method. Within that virtual method’s code you call the generated procedure that you’re trying to execute.

In the end, the base class will call the empty virtual method, but the derived method will be executed instead, and it calls the generated procedure in your app.

Of course, if you need to instantiate that class, then you’ll have to make sure you instantiate the derived class, not the original bass class, which may be challenging, but may work just fine.

Alternatively, you can define the procedure you need to call as a TYPE, then pass in a reference to it when you instantiate the bass class. Give me a few minutes and I’ll throw together an example program to demonstrate this.

Here’s the INC file for your base class. Note the MAP statement in the class header, where you define what your external procedure is going to look like, and the reference to that type passed into the Init method.

                       MAP
BaseClass_OutsideProc    PROCEDURE(STRING S),LONG,TYPE  !Notice TYPE attribute, so this is just a description
                       END
                              
BaseClass              CLASS,TYPE,MODULE('BaseClass.clw'),LINK('BaseClass.clw',_ABCLinkMode_),DLL(_ABCDllMode_)
Init                     PROCEDURE(BaseClass_OutsideProc OutsideProcToBeCalled)  !Class is told what to call
Test                     PROCEDURE  !Class will use the procedure
                       END

Here’s the base class CLW. Note the reference to the external procedure will be stored in module data, as the compiler will not let you store it as class data. That’s not a problem, as you can still use a class method to initialize that module data. One the procedure reference is initialized, it can be called normally.

                MEMBER

  INCLUDE('BaseClass.inc'),ONCE

                MAP
                END

OutsideProc     &BaseClass_OutsideProc  !Reference to the external procedure

BaseClass.Init  PROCEDURE(BaseClass_OutsideProc OutsideProcToBeCalled)
  CODE
  OutsideProc &= OutsideProcToBeCalled  !Set the procedure reference in module memory

BaseClass.Test  PROCEDURE
  CODE
  IF OutsideProc &= NULL
    ASSERT(FALSE, 'BaseClass_OutsideProc is not initialized')
    HALT(1)
  END
  MESSAGE('OutsideProc returned '& OutsideProc('Hello World!'))  !Call the procedure

Finally, you can use it in your program. Instantiate the object normally, and make sure to call the Init method to set the module’s reference to the external procedure. Note that it’s up to you to ensure that the prototypes match.

                PROGRAM

  INCLUDE('BaseClass.inc'),ONCE

                MAP
MyOutsideProc     PROCEDURE(STRING S),LONG  !Prototype must match BaseClass_OutsideProc
                END

MyObject        BaseClass

  CODE
  MyObject.Init(MyOutsideProc)  !Tell the BaseClass about the external procedure
  MyObject.Test()
  
MyOutsideProc   PROCEDURE(STRING S)!,LONG
  CODE
  RETURN LEN(S)
3 Likes

I will try these methods.

Thank you.
Regards

1 Like

Hello,
I tried to fit your example in my code (although I know it is so clear) but I could not fit it well in my code as I got many errors (surely because of my misplacing your code).

when I added the sk_SplitPath inside the Map in the class, it compiled the application without errors.
But after I added the GetTempName Routine I got this error:

Unresolved External SK_SPLITPATH@Fsbl in sk_class1.obj (which is the minimum error I got in my tries)

I beleive it is so close.

please have a look at my code if you can help

thank you

!********************
! "cla\sk_class1.clw"
!********************
   Member
   Map
sk_SplitPath    PROCEDURE(string,long),string,type
   end
   Include('cla\sk_class1.inc'),ONCE
 
sk_class1.sk_NewFileName PROCEDURE(XmlName)
Temp_name   Cstring(255)
Tname       Cstring(255)
Ext         Cstring(20)
    Code
  Tname = sk_SplitPath(clip(XmlName),1) & '\' & sk_SplitPath(clip(XmlName),2)
  Ext   = sk_SplitPath(clip(XmlName),3)
  i# = 1
  Temp_name = Tname & '__' & format(i#,@n03) & '.' & Ext
  RETURN(Temp_name)
!********************
! "cla\sk_class1.inc"
!********************
sk_class1  CLASS,Type,Module('cla\sk_class1.clw'),LINK('cla\sk_class1.clw',_ABCLinkMode_),DLL(_ABCDllMode_)
sk_NewFileName              Procedure(string),string
           end  
!********************
GetTempName    Routine
     Data
sk      sk_class1
     Code
  stop(sk.sk_NewFileName(XmlName))
!********************

The error you have is raised by the linker. The compiler can strip off the unused code. So while you have not used an instance of the sk_class1 CLASS in the GetTempName ROUTINE, all the code for this CLASS could be missed in the produced object file. When the instance of the CLASS was used, its code was not stripped off and the linker - absolutely correctly - raised the error.

The sk_SplitPath procedure is declared in the MAP with the TYPE attribute. Such declaration does not places any record to the object file which linker could resolve as sk_SplitPath function.

Just remove the TYPE attribute from the sk_SplitPath declaration.

Hi,

I removed the Type attribute and got this error:
Missing procedure definition:sk_SplitPath

Regards

Procedures declared in the MAP out of any MODULE…END block must be implemented in the current source file. If function is external for the current source, put its declaration inside the MODULE…END block. Parameter of MODULE can be any string, for example

  MAP
    MODULE('')
      sk_SplitPath PROCEDURE(string,long),string
    END
  END

Sorry. Same old error:

Unresolved External SK_SPLITPATH@Fsbl in sk_class1.obj

Regards

Is this function implemented anywhere (another source file, linked library) in the project?

This function is one part of my own template which is added to the global extensions in the application and it works fine when I use it anywhere in the applications directly, and this code I trying to create as a class was originally a normal code in the application and I removed it in order to avoid any conflict. That’s why I am using it to create this class.

Regards

The function can’t be a part of template. It can be generated by the template or template can reference it in some way.

Is the sk_SplitPath function really implemented in some source/library file listed in the same project as the source module with your CLASS? If the linker can’t resolve the external name of some function or data object, this means that the function or data object with that external name found nowhere in linking OBJ/LIB files.

sk_SplitPath is a function defined in a template and the template is set in the global extensions and it is used all around the project and returns the requested values and this test class is the only class in the project as I creating it to test classes implementation for my future projects. Hope this answers your question.

This function simply splits the path to Filepath, filename, extension.
And I selected it because it is simple code and straight forward.

For Example:
this code compiles without error and runs the application as expected
image
this code compiles with the error mentioned before
image

Same routine but put a remark on the Class calling part.

Hello Everybody,
I think the case is getting more confusing because of the sk_SplitPath function which comes from my template, so I decided to create a new fresh application with no templates used, and created a local source procedure called sk_SplitPath with the proper parameters and set the “Declare Globally flag” ON, also I did not add any code in that procedure except returning the values received.

But still the same issues.

Words have the sense. A function can’t be defined in templates. Its code can be generated by templates or templates can generate some reference to a function. For example, several functions (CheckOpen, ReportPreview, etc.) are generating by templates in STDFUNC.TPW, but they are not defined in this template file.

Templates are processing by the AppGen and cab used to generate source files. Everything in (Clarion) source files, including files generated by templates, is compiling by the Clarion compiler. The compiler either produces an object file if source has been compiled without errors or reports errors. Objects files are linked by the linker to a DLL or EXE file and the linker resolves all references to functions and to static data objects during this process. The error you’ve mentioned earlier is reported by the linker. This error means that the function with prototype

sk_SplitPath PROCEDURE (STRING, LONG),<some return type>

is referenced in the sk_class1.clw but none object file from the list of files to be linked contains the implementation of such function.

If so, the sk_SplitPath function has incorrect prototype in sk_class1.clw (for example, the second parameter actually has the type other than LONG). But sk_SplitPath has another - correct - prototype in the scope of the GetTempName ROUTINE. If you’ll post the object file produced by compiling the source file containing the GetTempName ROUTINE, I could write the actual prototype of sk_SplitPath.

Hello Also,

I already simplified the whole matter in order to minimize the factors:
1 - I excluded the template by creating new fresh app
2 - I created sk_SplitPath2 procedure locally and declared it globally then I changed the values in the class files from sk_SplitPath to sk_SplitPath2
3 - I added only one parameter in sk_SplitPath2 which is “string”
4 - the sk_SplitPath2 procedure contains only one line of code that returns the received value
5 - the GetTempName Routine is only calling the procedure from class (it can be anything).

So now nothing is special or depending on my coding about the case as nothing is required or depending on my environment (except I am using clarion 6.3)

After all this I am still giving the same errors.

Regards
and thank for the follow-up