Poll: Is it worth creating a generic MACRO library while we wait for templates?
You do not have permission to vote in this poll.
Yes, and I would definitely have a use for it
100.00%
10 100.00%
Yes, but I probably use very little of it, if at all.
0%
0 0%
I don\'t know.
0%
0 0%
No, I\'d rather wait for template support.
0%
0 0%
Total 10 vote(s) 100%
* You voted for this item. [Show Results]

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Standard MACRO Library
#1
Here is a question I've been pondering while I work on a project of mine, something of a generic library akin to C++'s STL: How many people would use a generic MACRO library? While implementing it is rather tedious without the support of classes, it's not impossible, and not too far from being quite practical.

Currently, I have implemented a vector and stack containers, and I've started working on generic binary comparison functions. I'll present some code at the end of the post.

Now, mind you, I have not yet implemented custom allocators (seems silly to do so without classes), nor iterators (so I have yet to begin any generic algorithm functions dealing with iterators such as find() or copy()), so use of these containers is pretty raw. My goal is to implement vector and stack iterators sometime before Christmas. That will at least give me an opportunity to start coding the generic algorithms.

As a demonstration as to how something like this would look, here is code for some generic comparison functions:

Code:
'' -----------------------------------------------------------------------------
'' - sml/comparison.bi -
'' -----------------------------------------------------------------------------
'' contains binary function MACROs for global comparisons

#ifndef COMPARISON_BI
#define COMPARISON_BI

option explicit
option byval

#include "utility/macros.bi"    '' for INST_MACRO_FOR_INTTYPES(...)

enum boolean : false = 0 : true = not false : end enum

declare function not_equal overload () as boolean
declare function less_than overload () as boolean
declare function less_than_or_equal overload () as boolean
declare function equal overload () as boolean
declare function greater_than overload () as boolean
declare function greater_than_or_equal overload () as boolean

'' not_equal( lhs as T, rhs as T ) as boolean
'' -----------------------------------------------------------------------------
'' returns true if lhs is not equal to rhs
#define INST_NOT_EQUAL( T ) _
function not_equal( lhs as T, rhs as T ) as boolean :_
   return lhs <> rhs :_
end function

'' less_than( lhs as T, rhs as T ) as boolean
'' -----------------------------------------------------------------------------
'' returns true if lhs is less than rhs
#define INST_LESS_THAN( T ) _
function less_than( lhs as T, rhs as T ) as boolean :_
   return lhs < rhs :_
end function

'' less_than_or_equal( lhs as T, rhs as T ) as boolean
'' -----------------------------------------------------------------------------
'' returns true if lhs is less than or equal to rhs
#define INST_LESS_THAN_OR_EQUAL( T ) _
function less_than_or_equal( lhs as T, rhs as T ) as boolean :_
   return lhs <= rhs :_
end function

'' equal( lhs as T, rhs as T ) as boolean
'' -----------------------------------------------------------------------------
'' returns true if lhs is equal to rhs
#define INST_EQUAL( T ) _
function equal( lhs as T, rhs as T ) as boolean :_
   return lhs = rhs :_
end function

'' greater_than( lhs as T, rhs as T ) as boolean
'' -----------------------------------------------------------------------------
'' returns true if lhs is greater than rhs
#define INST_GREATER_THAN( T ) _
function greater_than( lhs as T, rhs as T ) as boolean :_
   return lhs > rhs :_
end function

'' greater_than_or_equal( lhs as T, rhs as T ) as boolean
'' -----------------------------------------------------------------------------
'' returns true if lhs is greater than or equal to rhs
#define INST_GREATER_THAN_OR_EQUAL( T ) _
function greater_than_or_equal( lhs as T, rhs as T ) as boolean :_
   return lhs >= rhs :_
end function

'' - instantiate comparison function MACROs for integral and string types ------
INST_MACRO_FOR_INTTYPES( NOT_EQUAL )                : INST_NOT_EQUAL( string )
INST_MACRO_FOR_INTTYPES( LESS_THAN )                : INST_LESS_THAN( string )
INST_MACRO_FOR_INTTYPES( LESS_THAN_OR_EQUAL )       : INST_LESS_THAN_OR_EQUAL( string )
INST_MACRO_FOR_INTTYPES( EQUAL )                    : INST_EQUAL( string )
INST_MACRO_FOR_INTTYPES( GREATER_THAN )             : INST_GREATER_THAN( string )
INST_MACRO_FOR_INTTYPES( GREATER_THAN_OR_EQUAL )    : INST_GREATER_THAN_OR_EQUAL( string )

#endif '' COMPARISON_BI
As you can see, these function MACROs compare two values of like types, and are instantiated (expanded) for integral and string types by default. The file macros.bi contains the space-saving MACRO INST_MACRO_FOR_INTTYPES(...), and is defined below:

Code:
'' -----------------------------------------------------------------------------
'' - sml/utility/macros.bi -
'' -----------------------------------------------------------------------------
'' - defines useful macros for instantiating function MACROs
'' -
'' - function MACRO declaration conventions:
'' -    unary function MACROs =
:_ -        #define INST_UFM_templatename( type ) _
:_ -        function templatename overload ( ... ) ...
'' -    binary function MACROs =
:_ -        #define INST_BFM_templatename( type1, type2 ) _
:_ -        function templatename overload ( ... ) ...

#ifndef MACROS_BI
#define MACROS_BI

'' MACRO for instantiating unary MACROs for all integral types
#define INST_MACRO_FOR_INTTYPES( macro ) _
INST_##macro##( byte ) :_
INST_##macro##( ubyte ) :_
INST_##macro##( short ) :_
INST_##macro##( ushort ) :_
INST_##macro##( integer ) :_
INST_##macro##( uinteger ) :_
INST_##macro##( longint ) :_
INST_##macro##( ulongint )

'' MACRO for instantiating unary MACROs for floating-point types
#define INST_MACRO_FOR_FLOATTYPES( macro ) _
INST_##macro##( single ) :_
INST_##macro##( double ) :_

'' MACRO for instantiating unary MACROs for all standard types
#define INST_MACRO_FOR_STDTYPES( macro ) _
INST_##macro##( byte ) :_
INST_##macro##( ubyte ) :_
INST_##macro##( short ) :_
INST_##macro##( ushort ) :_
INST_##macro##( integer ) :_
INST_##macro##( uinteger ) :_
INST_##macro##( longint ) :_
INST_##macro##( ulongint ) :_
INST_##macro##( single ) :_
INST_##macro##( double ) :_
INST_##macro##( string ) :_
INST_##macro##( zstring ptr )

#endif '' MACROS_BI

Moving on, another part of the library that makes use of the above comparison functions is the always useful max(...) and min(...) generic functions. For space reasons, I'll just post the max(...) definition, min(...) being similar:

Code:
'' -----------------------------------------------------------------------------
'' - sml/utility/max.bi -
'' -----------------------------------------------------------------------------
'' defines a function MACRO that returns the greater of two objects

#ifndef MAX_BI
#define MAX_BI

option explicit
option byval

#include "../comparison.bi"     '' for less_than(...)
#include "macros.bi"            '' for INST_MACRO_FOR_INTTYPES(...)

declare function max overload()

'' - max( T,T ) as T -----------------------------------------------------------
'' returns the greater of two objects. objects must be less_than comparable
#define INST_MAX( T ) _
:_ '' returns the value of the greater of two objects
function max( a as T, b as T ) as T :_
   if( less_than( a,b ) ) then return b : end if :_
   return a :_
end function :_
:_
:_ '' returns a reference to the greater of two objects
function max( a as T ptr, b as T ptr ) as T ptr :_
   if( less_than( *a,*b ) ) then return b : end if :_
   return a :_
end function

INST_MACRO_FOR_INTTYPES( MAX )

#endif '' MAX_BI
This function MACRO uses the generic comparison function less_than(...) to determine the maximum of two values. It is also specialized for references as well. To use this function with a type of your own, your type must be less than comparable, that is, you must overload the global less_than(...) function for your type. As an example, I have done just that with a simple Rational number type:

Code:
'' -----------------------------------------------------------------------------
'' - sml/smlTest.bas -
'' -----------------------------------------------------------------------------
'' demonstrates two generic comparison functions, and how to use max(...) and
'' min(...) with UDTs

#include "comparison.bi"
#include "utility/max.bi"
#include "utility/min.bi"

option explicit
option byval

'' define a Rational number type
type Rational
    numerator as integer
    denominator as integer
end type

'' overload less_than(...) function for Rational types [used by max(...)]
function less_than( lhs as Rational, rhs as Rational ) as boolean
    return ( lhs.numerator/lhs.denominator ) < ( rhs.numerator/rhs.denominator )
end function

'' overload greater_than(...) function for Rational types [used by min(...)]
function greater_than( lhs as Rational, rhs as Rational ) as boolean
    return ( lhs.numerator/lhs.denominator ) > ( rhs.numerator/rhs.denominator )
end function

'' instantiate max(...) and min(...) function MACROs for Rational types
INST_MAX( Rational )
INST_MIN( Rational )

    dim as Rational r1 => ( 1,2 )
    dim as Rational r2 => ( 1,3 )

    assert( less_than( r2, max( r1,r2 ) ) )         '' test max(...)
    assert( less_than( r2, *max( @r1,@r2 ) ) )
    assert( greater_than( r1, min( r1,r2 ) ) )      '' test min(...)
    assert( greater_than( r1, *min( @r1,@r2 ) ) )
    sleep : end 0
This code simply defines a custom type, makes it less than and greater than comparable by overloading the less_than(...) and greater_than(...) functions, and finally instantiates Rational versions of the max(...) and min(...) function MACROs. This would be what end-user code might look like.

You'll note that I haven't taken into account floating-point types, or zstrings for the generic comparison functions - yet. That is also planned this week, as well as a gigantic Christmas dinner I will be having when I go home this weekend.

If you care to share, I've filled out a poll - I don't know if it will work or not - and I'd like to get some feedback on this idea. Thanks for your time.
stylin:
Reply
#2
that's pretty neat. i was wondering how long it will take until someone starts on something like this for fb :p the only hurdle now is that ppl might need a good tutorial on generic programming :p
quote="NecrosIhsan"]
[Image: yagl1.png]
[/quote]
Reply
#3
It's a good idea, but when templates are added, I'll probably use the templated version more than the macro version. But until templates come out, I'll be glad to use these.

I've been waiting for an FB version of std::vector for ages...
.14159265358979323846264338327950288419716939937510582709445
Glarplesnarkleflibbertygibbertygarbethparkentalelelangathaffendoinkadonkeydingdonkaspamahedron.
Reply
#4
@ marzecTIM : Definitely. Would you like to do the honors? Give me a couple days to get iterators for vector and stack and I'll show you what I have. Next thing on my wishlist is namespaces ... sml::vector, sml::list Smile

@ thegrogen : Yes, templates will pretty much make this library obsolete, although I'm working towards making it an easy transition when we get classes - I don't think I'll have to rewrite very much of the code.

Mainly I wanted to have this library so I wouldn't have to keep writing custom generic containers for every project I have.
stylin:
Reply
#5
Quote:@ marzecTIM : Definitely. Would you like to do the honors? Give me a couple days to get iterators for vector and stack and I'll show you what I have. Next thing on my wishlist is namespaces ... sml::vector, sml::list Smile

ok ok, here's a crazy thought: why don't you use c++ again? you can get rid of a lot of c++ syntax by replacing it with basic equivalents via macros ( i think i once had an include file doing this ). also the namespace sml is somewhat confusing if you know what sml can stand for. and i'm not enough into the fb community to actually write a tutorial on something 2% will use. still keep up the good work it's the first use of macros actually being usefull

edit: just noticed, still no reply on fb.net to your thread there. i guess that speaks for my point and i personally think that's really sad
quote="NecrosIhsan"]
[Image: yagl1.png]
[/quote]
Reply
#6
:roll:

Why must everyone make such a deal about us giving good ol' BASIC some better functionality. Adding on features has nothing to do with making it more like C++. People don't have to use those features. Just live with it. Sheesh!
quote="Deleter"]judging gameplay, you can adaquately compare quake 4 with pong[/quote]
Reply
#7
Quote:edit: just noticed, still no reply on fb.net to your thread there. i guess that speaks for my point and i personally think that's really sad

im sure he specified people to come over here, to avoid having two threads on the go. plus fb.net is a lot slower than this forum. i will make a comment, just as soon as i understand fully what it is! im sure there arre others like me, who are still trying to figure it out, and research the subject, so dont want to post yet.
EVEN MEN OF STEEL RUST.
[Image: chav.gif]
Reply
#8
i *kind of* understand what you're doing... basically somewhat of an operator overload, using macros, no? personally, the code i write doesn't have much use for tghis, but im sure a lot of people writing code could find such things useful. thanks for the time put into doing this... even if it's all made moot one day, it doesn't make it less useful atm :)
Reply
#9
@ Cha0s : The project's goal is basically to port the templated part of the C++ Standard Library - the STL - over to FreeBASIC via MACROs. Templated code is often used to replace polymorphic code; the functionality you gain through run-time polymorphism using virtual functions is replicated - and made safer - with compile-time polymorphism using templates.

Much of the STL deals with container types - such as vectors (dynamic arrays), linked lists, sets, queues, etc. - iterators - which are used to traverse any container (even raw arrays) - and algorithms - such as copy, find, remove, shuffle, etc., that use iterators to work with any container (even raw arrays).

If you've ever written some kind of container - a linked list to store world objects, for instance - then you'd benefit from using STL's container types, which are templated classes that allow you to store any type of element without changing the container itself. The containers are fully dynamic, so there's no need to worry about such things as reallocation of arrays.

Often, the types of objects that can be used in templated code require the objects to have certain properties - such as being less than or greater than comparable. The code I posted shows some examples of a few comparison requirement fullfillments. The great thing about templated code is all checks happen at compile-time, so you can never pass an object to a function template that doesn't meet the requirements of the template. As you can see, there's no need to explicitly state these requirements, we can just use objects how we want to use them - by using them as arguments in a call to less_than(...), for example - and the compiler checks to see if the object can indeed be passed to that function.

This behaviour is the opposite to that of dynamic polymorphism, where type checking occurs at run-time. Using ANY pointers is also severely unsafe, and does not allow type checks at compile-time. I stay as far away from ANY pointers as I can.

Back to algorithms for a second, if you've ever sorted an array, then you'll quickly see how the STL can help you. With one function, the STL can sort the elements in a container, that is, any type of container holding any type of element - one function. Also, by supplying std:Confusedort with a predicate (basically a function), you can choose how std:Confusedort sorts those elements.

There is a whole world of useful - often necessary - functionality in the STL, and I'm trying to bring it to FreeBASIC using MACROs, although the task is a little more arduous than I had orginally thought it to be. Without classes - and therefore inheritance - it's going to be awhile before the entire source is ported.

After having found it necessary to include the STL concept of allocators - objects that deal with the allocation, reallocation and deallocation of memory, as well as the construction and destruction of objects - I am now currently working on porting that over. Once that's done, I will implement Iterators, and then all of the containers and algorithms will be quite easy.
stylin:
Reply
#10
I would prefer a macro rewriting tool that allowed me to use spaces and separate lines in the rewriting.
Peace cannot be obtained without war. Why? If there is already peace, it is unnecessary for war. If there is no peace, there is already war."

Visit www.neobasic.net to see rubbish in all its finest.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)