casts

This is the forum for miscellaneous technical/programming questions.

Moderator: 2ffat

casts

Postby theLizard » Mon Jul 12, 2010 5:41 pm

Is there a way to cast a TObject to another type using the ClassType property, for example;

TLizardNode *node = static_cast<TObject->ClassType()>(in);

I am sure I now what the answer will be but thought I would ask anyway..
theLizard
BCBJ Guru
BCBJ Guru
 
Posts: 186
Joined: Wed Mar 18, 2009 2:14 pm

Re: casts

Postby gambit47 » Wed Jul 14, 2010 12:21 am

theLizard wrote:Is there a way to cast a TObject to another type using the ClassType property


No. Such type-casts are evaluated at compile-time, and as such need a class type known at compile-time, not dynamically at run-time. Besides, you are casting the wrong way to begin with. You need to specify the class type you are casting to, which in this case is a TLizardNode and is known at compile-time:

Code: Select all
TLizardNode *node =  static_cast<TLizardNode*>(in);


Or:

Code: Select all
TLizardNode *node =  dynamic_cast<TLizardNode*>(in);
if( node != NULL ) ...
Remy Lebeau (TeamB)
http://www.lebeausoftware.org
User avatar
gambit47
BCBJ Author
BCBJ Author
 
Posts: 472
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: casts

Postby theLizard » Wed Jul 14, 2010 1:41 am

No. Such type-casts are evaluated at compile-time, and as such need a class type known at compile-time, not dynamically at run-time. Besides, you are casting the wrong way to begin with. You need to specify the class type you are casting to, which in this case is a TLizardNode and is known at compile-time:


TLizardNode *node = static_cast<TObject->ClassType()>(in); this was only a given as an example of what would have been a wishful way to do it, I expected that it could not be done but thought I would ask just in case there was a way.

Thanks for the reply.
theLizard
BCBJ Guru
BCBJ Guru
 
Posts: 186
Joined: Wed Mar 18, 2009 2:14 pm

Re: casts

Postby gambit47 » Wed Jul 14, 2010 4:29 pm

theLizard wrote:TLizardNode *node = static_cast<TObject->ClassType()>(in); this was only a given as an example of what would have been a wishful way to do it, I expected that it could not be done but thought I would ask just in case there was a way.


ClassType() is a run-time value. You cannot do type-casting with it. Even if you could (which would not be meaningful), you would have no way of accessing the class members after the cast, since ClassType() returns a generic TMetaClass* pointer.

What exactly are you trying to accomplish?
Remy Lebeau (TeamB)
http://www.lebeausoftware.org
User avatar
gambit47
BCBJ Author
BCBJ Author
 
Posts: 472
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: casts

Postby theLizard » Wed Jul 14, 2010 6:26 pm

What exactly are you trying to accomplish?


Wanted to see if there was a way to cast a generic TObject to a class without having to explicitly name the class using the TObjects ClassType() or CLassName() or Class anything in functions calls to maybe simplify the following

Code: Select all
int __fastcall   TLizardAgent::SetEditOrdinals(TWinControl *p)
   {

   int ordinal;
   UnicodeString name1, name2;
   int type;
   int j;
   fieldCount = 0;
   //compare the edit controls on the TWinControl using ClassName, if they match one of these
   //cast the control to one of the TLizard controls e.g. TLizardComboBox * c = static_cast<TLizardComboBox *>(p->Controls[i]);
   //once cast to the edit control we can get the exended properties and set their values
   //from the info obtained from the database.  These were obtained with a call to TLizardAgent->getColDef(db, "vessels", "");

   //type:    is an enum
   //name1:    is the FieldName property of the control
   //name2:    is the field name extracted from the database and placed in Fields[n].field

   for(int i =0; i<p->ControlCount; i++)
      {
      type = coNone;
      if(p->Controls[i]->ClassName() == "TLizardComboBox")
         {
         c = static_cast<TLizardComboBox *>(p->Controls[i]);
         name2 = c->FieldName;
         type = coCombo;
         }
      if(p->Controls[i]->ClassName() == "TLizardEdit")
         {
         e = static_cast<TLizardEdit *>(p->Controls[i]);
         name2 = e->FieldName;
         type = coEdit;
         }
      if(p->Controls[i]->ClassName() == "TLizardDateTimePicker")
         {
         dt = static_cast<TLizardDateTimePicker *>(p->Controls[i]);
         name2 = dt->FieldName;
         type = coDateTime;
         }

      switch(type)
         {
         case coCombo:
         case coEdit:
         case coDateTime:
            for(j = 0; j< (int)Fields.size()-1; j++)
               {
               name1 = Fields[j]->Field.LowerCase();

               if(name1 == name2.LowerCase())
                  {
                  ordinal = Fields[j]->OrdinalPosition;
                  fieldCount++;
                  break;
                  }
               }
            switch(type)
               {
               case coCombo:
                  c->DbOrdinal = ordinal;
                  Fields[j]->Lookup = c->Lookup;
                  Fields[j]->LookupTable = c->LookupTable;
                  Fields[j]->LookUpIdentName = c->LookUpIdentName;
                  break;
               case coEdit:
                  e->DbOrdinal = ordinal;
                  break;
               case coDateTime:
                  dt->DbOrdinal = ordinal;
                  break;
               }
            break;
         }
      }
   return(fieldCount-1);
   }
//---------------------------------------------------------------------------
void __fastcall TLizardAgent::FillRecord(TLizardODBConnect *db, TWinControl *p, UnicodeString sql)
   {
   int i = 0, numbFields;
   SQLINTEGER count = 1;
   sqlConnect q;
   SQLCHAR val[STR_LEN];
   SQLINTEGER cb;

   AnsiString dsn;
   AnsiString s = sql;
   SQLRETURN retcode;
   SQLPOINTER rgbValue = &i;
   bool found = false;

   dsn = db->ODBCDatasourceName;
   db->AllocHandles(&q);
   //number of Fields to load
   numbFields = Fields.size();


      // SQL Native Client - for now
      q.retcode=SQLConnect(q.hdbc,(SQLCHAR*)dsn.c_str() /*dbInfo.odbcDbName.c_str()*/ ,SQL_NTS,NULL,0,NULL,0);

   //SQLRETURN SQLConnect(ConnectionHandle, ServerName, NameLength1, UserName, NameLength2,Authentication,NameLength3);

      q.retcode = SQLExecDirect(q.hstmt, s.c_str(), SQL_NTS);

      if (!MYSQLSUCCESS(q.retcode))
         {
         //error_out(SQL_HANDLE_STMT, q.hstmt);
         }
    if (q.retcode == SQL_SUCCESS || q.retcode == SQL_SUCCESS_WITH_INFO)
       {

       while (TRUE)
          {
          q.retcode = SQLFetch(q.hstmt);
          //if (q.retcode == SQL_ERROR || q.retcode == SQL_SUCCESS_WITH_INFO)
             //error_out(SQL_HANDLE_DBC, q.hdbc);

          if(q.retcode == SQL_SUCCESS || q.retcode == SQL_SUCCESS_WITH_INFO)
             {
            //ordinarily you would specify the field name and data type you want to read from the database
            //but this is no good since I do not want to have a function for every table in a database
            //so we tell the database to return the value as an SQL_C_CHAR and fill the Fields[x].value with
            //the result returned by val.
            //count is the field number (ordinal) so while the field count
            //does not match the number of Fields in Fields.size() get the field[n] value

             while(count != numbFields)
                  {
                  SQLGetData(q.hstmt, count+1, SQL_C_CHAR, &val, STR_LEN, &cb);
                  Fields[count]->Value = (char *)val;
                  count++;
                  }
             }
          else
            {
            break;
            }
          }
       if (q.retcode == SQL_SUCCESS || q.retcode == SQL_SUCCESS_WITH_INFO || SQL_NO_DATA) {
            SQLCancel(q.hstmt);
            SQLFreeHandle(SQL_HANDLE_STMT, q.hstmt);
            }
       SQLDisconnect(q.hdbc);

       }
    //we can now populate the edit controls which shows the values from the record.
    ShowRecord(db, p);
   }
//---------------------------------------------------------------------------
void __fastcall TLizardAgent::ShowRecord(TLizardODBConnect *db, TWinControl *p)
   {
   TDateTime DT;
   UnicodeString name1, name2, value, sql;
   int type;
   int j;
   //This is pretty much a reversal of making an sql statement, we have all the field values
   //in the Fields vector so put the value into the relative edit control. simple!!
   for(int i =0; i<p->ControlCount; i++)
      {
      type  = coNone;
      if(p->Controls[i]->ClassName() == "TLizardComboBox")
         {
         c = static_cast<TLizardComboBox *>(p->Controls[i]);
         name2 = c->FieldName;
         type = coCombo;
         }
      if(p->Controls[i]->ClassName() == "TLizardEdit")
         {
         e = static_cast<TLizardEdit *>(p->Controls[i]);
         name2 = e->FieldName;
         type = coEdit;
         }
      if(p->Controls[i]->ClassName() == "TLizardDateTimePicker")
         {
         dt = static_cast<TLizardDateTimePicker *>(p->Controls[i]);
         name2 = dt->FieldName;
         type = coDateTime;
         }
      switch(type)
         {
         case coCombo:
         case coEdit:
         case coDateTime:
            for(j = 0; j < (int)Fields.size(); j++)
               {
               name1 = Fields[j]->Field.LowerCase();

               if(name1 == name2.LowerCase())
                  {
                  value = Fields[j]->Value;
                  break;
                  }
               }
            switch(type)
               {
               case coCombo:
                  //is the combo a lookup list?
                  //if the combo box control shows values from a lookup table and the value
                  //we have obtained in this function for this field is an identity field we need to
                  //show the text value in the combo box.

                  //Note: if TlizardComboBox is a lookup and the text value entered does not exist in the database
                  //you are prompted to add the new value or abort, if you abort, the whole action will be aborted
                  //on the basis that you cannot have a lookup without an index into the lookup table.

                  if(c->Lookup)
                     {
                     //we have the field name, table name, the identity field name and the value
                     //create the sql and find the text value for the id value
                     sql = "SELECT " + c->LookUpFieldName + " FROM " + c->LookupTable + " WHERE ";
                     sql += c->LookUpIdentName + " = " + value;
                     value = GetFieldValue(db, sql);
                     //value now has the field value in field name, now we iterate through the combo box list
                     //to find the matching value then set the index into the combobox list so that it appears in
                     //the text part of the combo box.
                     for(int k =0; k<c->Items->Count; k++)
                        {
                        if(c->Items->Strings[k] == value)
                           {
                            c->ItemIndex = k;
                            break;
                           }
                        }
                     }
                  else
                     {
                     //can't see a combo being used for any other reason, if need to will look at this
                     //at that time.
                     }
                  break;
               case coEdit:
                  e->Text = FormatValue(value, Fields[j]->DataType);
                  break;
               case coDateTime:
                  if(value.Length() > 10)
                     value = value.SubString(1,10);  //only want the date part, this should always be formated with a 4 digit year
                  dt->Date = SetDate(value);
                  break;
               }
            break;
         }
      }
   }
//---------------------------------------------------------------------------



Note: The functions in the example code works perfectly, I do not need to write any code on any form having to get the data from tables and populate the associated edit controls. My TLizardAgent handles all of this with 4 lines of code on the form with

agent->GetColDef(db, "vessels", ""); //db is a TLizardOdbcConnect component that handles database connections
agent->SetEditOrdinals(p); //p is a TPanel with the edit controls. can be any TWinControl descendant
sql = "SELECT * FROM vessels WHERE id = " + IntToStr(agent->RecordId);
agent->FillRecord(db, p, sql);

But obviously that is not doable so will accept that my generic function calls will need to test TObjects class name and cast accordingly.

Thanks for your comments, this is how I learn.
theLizard
BCBJ Guru
BCBJ Guru
 
Posts: 186
Joined: Wed Mar 18, 2009 2:14 pm

Re: casts

Postby gambit47 » Wed Jul 14, 2010 9:43 pm

theLizard wrote:Wanted to see if there was a way to cast a generic TObject to a class without having to explicitly name the class using the TObjects ClassType() or CLassName() or Class anything in functions calls to maybe simplify the following


Such logic can be simplified and generalized using dynamic_cast and RTTI, which are both accessible at runtime. For example:

Code: Select all
#include <TypInfo.hpp>

int __fastcall TLizardAgent::SetEditOrdinals(TWinControl *p)
{
   String name;
   TControl *ctrl;
   PPropInfo propinfo;
   TLizardComboBox *cb;

   fieldCount = 0;

   //compare the edit controls on the TWinControl, if they match one of these
   //we can get the exended properties and set their values from the info
   //obtained from the database.  These were obtained with a call to
   //TLizardAgent->getColDef(db, "vessels", "");

   for(int i = 0; i < p->ControlCount; ++i)
   {
      ctrl = p->Controls[i];

      propinfo = GetPropInfo(ctrl, "FieldName");
      if( !propinfo )
         continue;

      name = GetStrProp(ctrl, propinfo);
      if( name.IsEmpty() )
         continue;

      for(int j = 0; j < (int)Fields.size(); ++j)
      {
         if( AnsiSameText(Fields[j]->Field, name) )
         {
            ++fieldCount;

            propinfo = GetPropInfo(ctrl, "DbOrdinal");
            if( propinfo )
               SetOrdProp(ctrl, propinfo, Fields[j]->OrdinalPosition);

            cb = dynamic_cast<TLizardComboBox*>(ctrl);
            if( cb != NULL )
            {
               Fields[j]->Lookup = cb->Lookup;
               Fields[j]->LookupTable = cb->LookupTable;
               Fields[j]->LookUpIdentName = cb->LookUpIdentName;
            }

            break;
         }
      }
   }

   return fieldCount;
}

void __fastcall TLizardAgent::FillRecord(TLizardODBConnect *db, TWinControl *p, AnsiString sql)
{
   int numbFields;
   SQLINTEGER count = 0;
   sqlConnect q;
   SQLCHAR val[STR_LEN];
   SQLINTEGER cb;

   AnsiString dsn = db->ODBCDatasourceName;

   db->AllocHandles(&q);

   //number of Fields to load
   numbFields = Fields.size();

   // SQL Native Client - for now
   q.retcode = SQLConnect(q.hdbc, (SQLCHAR*)dsn.c_str() /*dbInfo.odbcDbName.c_str()*/, SQL_NTS, NULL, 0, NULL, 0);

   //SQLRETURN SQLConnect(ConnectionHandle, ServerName, NameLength1, UserName, NameLength2,Authentication,NameLength3);

   q.retcode = SQLExecDirect(q.hstmt, sql.c_str(), SQL_NTS);

   if( !MYSQLSUCCESS(q.retcode) )
   {
      //error_out(SQL_HANDLE_STMT, q.hstmt);
   }

   if( (q.retcode == SQL_SUCCESS) || (q.retcode == SQL_SUCCESS_WITH_INFO) )
   {
      q.retcode = SQLFetch(q.hstmt);
      //if( (q.retcode == SQL_ERROR) || (q.retcode == SQL_SUCCESS_WITH_INFO) )
         //error_out(SQL_HANDLE_DBC, q.hdbc);

      if( (q.retcode == SQL_SUCCESS) || (q.retcode == SQL_SUCCESS_WITH_INFO) )
      {
         //ordinarily you would specify the field name and data type you want to read from the database
         //but this is no good since I do not want to have a function for every table in a database
         //so we tell the database to return the value as an SQL_C_CHAR and fill the Fields[x].value with
         //the result returned by val.
         //count is the field number (ordinal) so while the field count
         //does not match the number of Fields in Fields.size() get the field[n] value

         while( count != numbFields )
         {
            SQLGetData(q.hstmt, count+1, SQL_C_CHAR, &val, STR_LEN, &cb);
            Fields[count]->Value = AnsiString((char *)val, cb);
            ++count;
         }
      }

      SQLCancel(q.hstmt);
      SQLDisconnect(q.hdbc);
   }

   SQLFreeHandle(SQL_HANDLE_STMT, q.hstmt);

   //we can now populate the edit controls which shows the values from the record.
   ShowRecord(db, p);
}

void __fastcall TLizardAgent::ShowRecord(TLizardODBConnect *db, TWinControl *p)
   {
   String name, value, sql;
   TControl *ctrl;
   PPropInfo propinfo;

   TLizardComboBox *cb;
   TLizardEdit *e;
   TLizardDateTimePicker *dtp;

   //This is pretty much a reversal of making an sql statement, we have all the field values
   //in the Fields vector so put the value into the relative edit control. simple!!

   for(int i = 0; i < p->ControlCount; ++i)
   {
      ctrl = p->Controls[i];

      propinfo = GetPropInfo(ctrl, "FieldName");
      if( !propinfo )
         continue;

      name = GetStrProp(ctrl, propinfo);
      if( name.IsEmpty() )
         continue;

      for(int j = 0; j < (int)Fields.size(); ++j)
      {
         if( AnsiSameText(Fields[j]->Field, name) )
         {
            value = Fields[j]->Value;

            if( (cb = dynamic_cast<TLizardComboBox*>(ctrl)) != NULL )
            {
               //is the combo a lookup list?
               //if the combo box control shows values from a lookup table and the value
               //we have obtained in this function for this field is an identity field we need to
               //show the text value in the combo box.

               //Note: if TlizardComboBox is a lookup and the text value entered does not exist in the database
               //you are prompted to add the new value or abort, if you abort, the whole action will be aborted
               //on the basis that you cannot have a lookup without an index into the lookup table.

               if( cb->Lookup )
               {
                  //we have the field name, table name, the identity field name and the value
                  //create the sql and find the text value for the id value

                  sql = "SELECT " + cb->LookUpFieldName + " FROM " + cb->LookupTable + " WHERE " + cb->LookUpIdentName + " = " + value;
                  value = GetFieldValue(db, sql);

                  //value now has the field value in field name, now we iterate through the combo box list
                  //to find the matching value then set the index into the combobox list so that it appears in
                  //the text part of the combo box.

                  cb->ItemIndex = cb->Items->IndexOf(value);
               }
               else
               {
                  //can't see a combo being used for any other reason, if need to will look at this
                  //at that time.
               }
            }

            else if( (e = dynamic_cast<TLizardEdit*>(ctrl)) != NULL )
            {
               e->Text = FormatValue(value, Fields[j]->DataType);
            }

            else if( (dtp = dynamic_cast<TLizardDateTimePicker*>(ctrl)) != NULL )
            {
               if( value.Length() > 10 )
                  value = value.SubString(1, 10);  //only want the date part, this should always be formated with a 4 digit year
               dtp->Date = SetDate(value);
            }

            break;
         }
      }
   }
}
Remy Lebeau (TeamB)
http://www.lebeausoftware.org
User avatar
gambit47
BCBJ Author
BCBJ Author
 
Posts: 472
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: casts

Postby theLizard » Wed Jul 14, 2010 11:04 pm

Thanks Remy,

I would never have discovered your solution without this question, I can see how it works and will implement this.
theLizard
BCBJ Guru
BCBJ Guru
 
Posts: 186
Joined: Wed Mar 18, 2009 2:14 pm


Return to Technical

Who is online

Users browsing this forum: No registered users and 2 guests