Register for your free account! | Forgot your password?

Go Back   elitepvpers > Coders Den > C/C++
You last visited: Today at 00:13

  • Please register to post and access all features, it's quick, easy and FREE!

Advertisement



C++ Multithreading + Klassen

Discussion on C++ Multithreading + Klassen within the C/C++ forum part of the Coders Den category.

Reply
 
Old   #1
 
Shadow992's Avatar
 
elite*gold: 77
Join Date: May 2008
Posts: 5,430
Received Thanks: 5,878
C++ Multithreading + Klassen

Mein Problem ist es , dass ich eine Klasse habe (nehmen wir als Beispiel Auto)
und eine Funktion (Beispiel : BremswegBerechnen) habe , die aber in einem extra Thread laufen soll . Also so in etwa :

Code:
.
.
.
int main()
{
CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)BMW.BremswegBerechnen, 0, 0, 0);
}
.
.
.
Das das so nicht funktioniert , habe ich auch schon festgestellt , was ich leider nicht machen kann ist folgendes :

Code:
int main()
{
CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)test, 0, 0, 0);
}

void test()
{
BMW.BremswegBerechnen;
}
Das funktioniert aufgrund meiner momentanen Situation nicht
(Ich will den Code ja nicht für die Klasse Auto haben , sondern für eine andere bereits vorhandene Klasse , würde ich aber mein Code hier posten , würde das den Rahmen sprengen , da er recht umfangreich ist und sehr viele verwirren wird , weil darin Funktionen enthalten sind , die eig. nur sehr wenige kennen dürften)

Ich habe es bereits mit der "Klasse" Boost von dieser Seite probiert :

Leider kommen bei meinem Compiler ein paar Errors (ich benutze wxDev-C++ , mit VC++ habe ich die Klasse noch nicht probiert , da ich eigentlich nur sehr ungerne auf VC++ umsteigen möchte , vorallem weil mir die wxDev-C++ so sehr ans Herz gewachsen ist)

Hier mal die Errors :
Code:
Compiler: Default GCC compiler
Building Makefile: "C:\Dokumente und Einstellungen\Benutzername\Desktop\Neuer Ordner\Makefile.win"
Finding dependencies for file: C:\Dokumente und Einstellungen\Benutzername\Desktop\Neuer Ordner\main.cpp
Führt  make... aus
make.exe -f "C:\Dokumente und Einstellungen\Benutzername\Desktop\Neuer Ordner\Makefile.win" all
g++.exe -c main.cpp -o main.o -I"C:/Programme/Dev-Cpp/lib/gcc/mingw32/3.4.5/include"  -I"C:/Programme/Dev-Cpp/include/c++/3.4.5/backward"  -I"C:/Programme/Dev-Cpp/include/c++/3.4.5/mingw32"  -I"C:/Programme/Dev-Cpp/include/c++/3.4.5"  -I"C:/Programme/Dev-Cpp/include"  -I"C:/Programme/Dev-Cpp/"  -I"C:/Programme/Dev-Cpp/include/common/wx/msw"  -I"C:/Programme/Dev-Cpp/include/common/wx/generic"  -I"C:/Programme/Dev-Cpp/include/common/wx/html"  -I"C:/Programme/Dev-Cpp/include/common/wx/protocol"  -I"C:/Programme/Dev-Cpp/include/common/wx/xml"  -I"C:/Programme/Dev-Cpp/include/common/wx/xrc"  -I"C:/Programme/Dev-Cpp/include/common/wx"  -I"C:/Programme/Dev-Cpp/include/common"  -I"C:/Dev-Cpp/include"   

In file included from C:/Dev-Cpp/include/boost/thread/future.hpp:12,
                 from C:/Dev-Cpp/include/boost/thread.hpp:24,
                 from main.cpp:1:
C:/Dev-Cpp/include/boost/exception_ptr.hpp:43: error: looser throw specifier for `virtual boost::exception_ptr::~exception_ptr()'
C:/Dev-Cpp/include/boost/exception/detail/exception_ptr_base.hpp:27: error:   overriding `virtual boost::exception_detail::exception_ptr_base::~exception_ptr_base() throw ()'

make.exe: *** [main.o] Error 1

Ausführung beendet
}
und der dazugehörige Code (Ist ein beispiel COde , also sollte er funktionieren)
Code:
#include <boost/thread.hpp> 
#include <iostream> 

void wait(int seconds) 
{ 
  boost::this_thread::sleep(boost::posix_time::seconds(seconds)); 
} 

void thread() 
{ 
  for (int i = 0; i < 5; ++i) 
  { 
    wait(1); 
    std::cout << i << std::endl; 
  } 
} 

int main() 
{ 
  boost::thread t(thread); 
  t.join(); 
}
Die Verzeichnisse sind ganz sicher richtig gesetzt und es sind auch ganz sicher alle Datein vorhanden . Habe auch schon Neuinstallation von wxDev-C++ gemacht und es kamen die selben Errors , ich hoffe mir kann jemand helfen oder eine Alternative zu dieser Klasse geben ...

Danke schonmal im vorraus
Shadow992 is offline  
Old 01/14/2010, 12:19   #2
 
elite*gold: 0
Join Date: Aug 2006
Posts: 505
Received Thanks: 89
Deine erste Methode geht so:

PHP Code:
DWORD WINAPI ThreadFunc(LPVOID obj){
    
BMWobjekt = (BMW *)obj;
           
BMW->BremswegBerechnen("argument");
    return 
0;
}

auto BMW("argument""999");
auto.starteMotor();
auto.fahren(100"km/h");
HANDLE thread CreateThread(NULL0ThreadFunc, (LPVOID)&auto0NULL); 
Du musst einfach eine Referenz des Objektes an die Threadfunction übergeben, und daraus dann zurückcasten.
Es geht allerdings nicht, direkt in der Klasse eine Methode an den Thread zu übergeben (zumindest nicht mit CreateThread, sondern nur mit externen Klassen), sondern du müsstest eine Globale Funktion haben, und diese dann wie oben aus der Klasse heraus benutzen.

Aber erklär doch mal genauer was du machen willst, vllt. kann man es anders lösen.
kennyo is offline  
Thanks
1 User
Old 01/14/2010, 16:31   #3
 
Shadow992's Avatar
 
elite*gold: 77
Join Date: May 2008
Posts: 5,430
Received Thanks: 5,878
Quote:
Originally Posted by kennyo View Post
Deine erste Methode geht so:

PHP Code:
DWORD WINAPI ThreadFunc(LPVOID obj){
    
BMWobjekt = (BMW *)obj;
           
BMW->BremswegBerechnen("argument");
    return 
0;
}

auto BMW("argument""999");
auto.starteMotor();
auto.fahren(100"km/h");
HANDLE thread CreateThread(NULL0ThreadFunc, (LPVOID)&auto0NULL); 
Du musst einfach eine Referenz des Objektes an die Threadfunction übergeben, und daraus dann zurückcasten.
Es geht allerdings nicht, direkt in der Klasse eine Methode an den Thread zu übergeben (zumindest nicht mit CreateThread, sondern nur mit externen Klassen), sondern du müsstest eine Globale Funktion haben, und diese dann wie oben aus der Klasse heraus benutzen.

Aber erklär doch mal genauer was du machen willst, vllt. kann man es anders lösen.
Danke für deine Antwort .
Ich wollte eine Art Syntax Highlighting machen ...
Ich habe jetzt mal ein bisschen mit deinem Code rumprobiert , aber habe nicht wirklich eine Lösung gefunden ...

Bei mir ist das etwas komplizierter , als du es dargestellt hast ... Ich kopier mal das wichtigste ...
PHP Code:
AutoITEditDlg::AutoITEditDlg(wxWindow *parentwxWindowID id, const wxString &title, const wxPoint &position, const wxSizesizelong style)
wxDialog(parentidtitlepositionsizestyle


{
    
CreateGUIControls();      
    
HANDLE thread CreateThread(NULL0Highlight, (LPVOID)&WxRichTextCtrl10NULL);

  
}

void AutoITEditDlg::CreateGUIControls()
{
//Hier wird natürlich noch mehr gemacht , aber das brauchst du denke ich //mal nicht ...
    
WxRichTextCtrl1 = new wxRichTextCtrl(thisID_WXRICHTEXTCTRL1wxT(""), wxPoint(00), wxSize(797405), wxTAB_TRAVERSAL wxWANTS_CHARS wxVSCROLL wxHSCROLL wxNO_BORDER wxRE_MULTILINEwxDefaultValidatorwxT("WxRichTextCtrl1"));

}

void AutoITEditDlg::Highlight(LPVOID WxRichTextCtrl1)
{
//...
WxRichTextCtrl1->GetValue()
//...

So hatte ich es jetzt mal probiert ...
Es funktioniert aber nicht ...
Mit Klassen hatte ich bisher eigentlich keine Probleme ...

Danke schonmal im vorraus
Shadow992 is offline  
Old 01/14/2010, 17:58   #4


 
MrSm!th's Avatar
 
elite*gold: 7110
Join Date: Jun 2009
Posts: 28,908
Received Thanks: 25,409
Quote:
Originally Posted by Shadow992 View Post
Code:
int main()
{
CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)test, 0, 0, 0);
}

void test()
{
BMW.BremswegBerechnen;
}
nur mal nebenbei:

1. muss text() auch vor CreateThread definiert sein (also Prototyp reicht)
2. text() muss DWORD zurückgeben und die Calling Convetion WINAPI haben
MrSm!th is offline  
Old 01/15/2010, 19:04   #5
 
elite*gold: 0
Join Date: Aug 2006
Posts: 505
Received Thanks: 89
Mir fällt grad auf, dass ich was falsch gemacht habe.

statt

PHP Code:
DWORD WINAPI ThreadFunc(LPVOID obj){ 
    
BMWobjekt = (BMW *)obj
           
BMW->BremswegBerechnen("argument"); 
    return 
0


auto BMW("argument""999"); 
auto.starteMotor(); 
auto.fahren(100"km/h"); 
HANDLE thread CreateThread(NULL0ThreadFunc, (LPVOID)&auto0NULL); 
muss es

PHP Code:
DWORD WINAPI ThreadFunc(LPVOID obj){ 
    
BMWobjekt = (BMW *)obj
           
objekt->BremswegBerechnen("argument"); 
    return 
0


auto BMW("argument""999"); 
auto.starteMotor(); 
auto.fahren(100"km/h"); 
HANDLE thread CreateThread(NULL0ThreadFunc, (LPVOID)&auto0NULL); 
heissen.

Das funktioniert auf jedenfall, so steht es in einem meiner Programme.

Und zeig mir mal den Code der bei dir mit //... auskommentiert ist, denn da ist grade der Knackpunkt mit dem zurückcasten. Halte dich einfach an mein Beispiel. BMW ist da übrigens der Klassenname, was im nachhinein ein wenig verwirrend sein kann.
kennyo is offline  
Old 01/15/2010, 20:39   #6
 
Shadow992's Avatar
 
elite*gold: 77
Join Date: May 2008
Posts: 5,430
Received Thanks: 5,878
Ich habe es jetzt mal so probiert :
PHP Code:
 
#include <stdio.h>
#include <string>
#include "AutoITEditDlg.h"
#include <windows.h>
#include<wx/timer.h>

            
size_t sizef;
            
size_t len ;
            
int high=;
            
int high2=;
            
wxString stri;
            
wxString st;
            
wxString stringtofind;

DWORD WINAPI ThreadFunc(LPVOID obj)

 
wxRichTextCtrlWxRichTextCtrl1 = (wxRichTextCtrl *)obj
   while (
1==1)
 {
//Sleep(3000);
if (WxRichTextCtrl1->GetValue()!=stri  )
  {
if ( 
high==1)
   {
      
sizet=0;    
      
len 5;
      
stringtofind "Func ";

      
stri=WxRichTextCtrl1->GetValue();
      if(
sizef=stri.find (stringtofind,sizet) ==wxNOT_FOUNDstringtofind=stringtofind.Lower() ; 
      if(
sizef=stri.find (stringtofind,sizet) ==wxNOT_FOUNDstringtofind=stringtofind.Upper() ;         
      while(
stri.find (stringtofind,sizet) !=wxNOT_FOUND)
     {
        
sizef stri.find (stringtofind,sizet);
        
WxRichTextCtrl1->EndTextColour();
        
attr.SetTextColour(wxColour(00255));
        
WxRichTextCtrl1->SetStyle(sizef,sizef+len,attr);
        
sizet=sizef+len ;
        
WxRichTextCtrl1->BeginTextColour(wxColour(000));
     }
   }
  }
 }
}
AutoITEditDlg::AutoITEditDlg(wxWindow *parentwxWindowID id, const wxString &title, const wxPoint &position, const wxSizesizelong style)
wxDialog(parentidtitlepositionsizestyle


{
    
CreateGUIControls();      
    
WxRichTextCtrl1->Clear();    
    
WxRichTextCtrl1->BeginFontSize(8);
    
//WxRichTextCtrl1->BeginBold();
    //WxTimer1->Start(6000);
    
HANDLE thread CreateThread(NULL0ThreadFunc, (LPVOID)&WxRichTextCtrl10NULL); 

    

}

AutoITEditDlg::~AutoITEditDlg()
{


void AutoITEditDlg::CreateGUIControls()
{
    
//Do not add custom code between
    //GUI Items Creation Start and GUI Items Creation End.
    //wxDev-C++ designer will remove them.
    //Add the custom code before or after the blocks
    ////GUI Items Creation Start

    
WxTimer1 = new wxTimer();
    
WxTimer1->SetOwner(thisID_WXTIMER1);

    
WxButton5 = new wxButton(thisID_WXBUTTON5wxT("Number Highlighting On"), wxPoint(507449), wxSize(14328), 0wxDefaultValidatorwxT("WxButton5"));

    
WxButton1 = new wxButton(thisID_WXBUTTON1wxT("Functions Highlighting On"), wxPoint(506416), wxSize(14429), 0wxDefaultValidatorwxT("WxButton1"));

    
WxStaticText1 = new wxStaticText(thisID_WXSTATICTEXT1wxT("Ready"), wxPoint(59468), wxDefaultSize0wxT("WxStaticText1"));

    
WxButton4 = new wxButton(thisID_WXBUTTON4wxT("Look For Errors"), wxPoint(654415), wxSize(14064), 0wxDefaultValidatorwxT("WxButton4"));

    
WxButton3 = new wxButton(thisID_WXBUTTON3wxT("Read"), wxPoint(155413), wxSize(14164), 0wxDefaultValidatorwxT("WxButton3"));

    
WxButton2 = new wxButton(thisID_WXBUTTON2wxT("Write"), wxPoint(4411), wxSize(14854), 0wxDefaultValidatorwxT("WxButton2"));

    
WxRichTextCtrl1 = new wxRichTextCtrl(thisID_WXRICHTEXTCTRL1wxT(""), wxPoint(00), wxSize(797405), wxTAB_TRAVERSAL wxWANTS_CHARS wxVSCROLL wxHSCROLL wxNO_BORDER wxRE_MULTILINEwxDefaultValidatorwxT("WxRichTextCtrl1"));
    
WxRichTextCtrl1->SetMaxLength(0);
    
WxRichTextCtrl1->AppendText(wxT("WxRichTextCtrl1"));
    
WxRichTextCtrl1->SetFocus();
    
WxRichTextCtrl1->SetInsertionPointEnd();

    
SetTitle(wxT("AutoITEdit"));
    
SetIcon(wxNullIcon);
    
SetSize(8,8,804,523);
    
Center();
    
    
////GUI Items Creation End

Es funktioniert auch soweit , dass ich den Thread erstellen kann und er wird auch gestartet , aber sobald die erste Funktion kommt , für die das Objekt gebraucht wird , beendet sich das Programm ohne irgendwelche Errors oder ähnliches zu hinterlassen .

Edit :
Das ist nicht der komplette Code , aber wenn ich den kompletten Code poste , würde das in Unübersichtlichkeit enden und in Funktionen , die dir nicht bekannt sind
(auser du benutzt wxDevC++).
Shadow992 is offline  
Old 01/15/2010, 23:04   #7
 
elite*gold: 0
Join Date: Aug 2006
Posts: 505
Received Thanks: 89
Quote:
Originally Posted by Shadow992 View Post
Ich habe es jetzt mal so probiert :
PHP Code:
 
#include <stdio.h>
#include <string>
#include "AutoITEditDlg.h"
#include <windows.h>
#include<wx/timer.h>

            
size_t sizef;
            
size_t len ;
            
int high=;
            
int high2=;
            
wxString stri;
            
wxString st;
            
wxString stringtofind;

DWORD WINAPI ThreadFunc(LPVOID obj)

 
wxRichTextCtrlWxRichTextCtrl1 = (wxRichTextCtrl *)obj
   while (
1==1)
 {
//Sleep(3000);
if (WxRichTextCtrl1->GetValue()!=stri  )
  {
if ( 
high==1)
   {
      
sizet=0;    
      
len 5;
      
stringtofind "Func ";

      
stri=WxRichTextCtrl1->GetValue();
      if(
sizef=stri.find (stringtofind,sizet) ==wxNOT_FOUNDstringtofind=stringtofind.Lower() ; 
      if(
sizef=stri.find (stringtofind,sizet) ==wxNOT_FOUNDstringtofind=stringtofind.Upper() ;         
      while(
stri.find (stringtofind,sizet) !=wxNOT_FOUND)
     {
        
sizef stri.find (stringtofind,sizet);
        
WxRichTextCtrl1->EndTextColour();
        
attr.SetTextColour(wxColour(00255));
        
WxRichTextCtrl1->SetStyle(sizef,sizef+len,attr);
        
sizet=sizef+len ;
        
WxRichTextCtrl1->BeginTextColour(wxColour(000));
     }
   }
  }
 }
}
AutoITEditDlg::AutoITEditDlg(wxWindow *parentwxWindowID id, const wxString &title, const wxPoint &position, const wxSizesizelong style)
wxDialog(parentidtitlepositionsizestyle


{
    
CreateGUIControls();      
    
WxRichTextCtrl1->Clear();    
    
WxRichTextCtrl1->BeginFontSize(8);
    
//WxRichTextCtrl1->BeginBold();
    //WxTimer1->Start(6000);
    
HANDLE thread CreateThread(NULL0ThreadFunc, (LPVOID)&WxRichTextCtrl10NULL); 

    

}

AutoITEditDlg::~AutoITEditDlg()
{


void AutoITEditDlg::CreateGUIControls()
{
    
//Do not add custom code between
    //GUI Items Creation Start and GUI Items Creation End.
    //wxDev-C++ designer will remove them.
    //Add the custom code before or after the blocks
    ////GUI Items Creation Start

    
WxTimer1 = new wxTimer();
    
WxTimer1->SetOwner(thisID_WXTIMER1);

    
WxButton5 = new wxButton(thisID_WXBUTTON5wxT("Number Highlighting On"), wxPoint(507449), wxSize(14328), 0wxDefaultValidatorwxT("WxButton5"));

    
WxButton1 = new wxButton(thisID_WXBUTTON1wxT("Functions Highlighting On"), wxPoint(506416), wxSize(14429), 0wxDefaultValidatorwxT("WxButton1"));

    
WxStaticText1 = new wxStaticText(thisID_WXSTATICTEXT1wxT("Ready"), wxPoint(59468), wxDefaultSize0wxT("WxStaticText1"));

    
WxButton4 = new wxButton(thisID_WXBUTTON4wxT("Look For Errors"), wxPoint(654415), wxSize(14064), 0wxDefaultValidatorwxT("WxButton4"));

    
WxButton3 = new wxButton(thisID_WXBUTTON3wxT("Read"), wxPoint(155413), wxSize(14164), 0wxDefaultValidatorwxT("WxButton3"));

    
WxButton2 = new wxButton(thisID_WXBUTTON2wxT("Write"), wxPoint(4411), wxSize(14854), 0wxDefaultValidatorwxT("WxButton2"));

    
WxRichTextCtrl1 = new wxRichTextCtrl(thisID_WXRICHTEXTCTRL1wxT(""), wxPoint(00), wxSize(797405), wxTAB_TRAVERSAL wxWANTS_CHARS wxVSCROLL wxHSCROLL wxNO_BORDER wxRE_MULTILINEwxDefaultValidatorwxT("WxRichTextCtrl1"));
    
WxRichTextCtrl1->SetMaxLength(0);
    
WxRichTextCtrl1->AppendText(wxT("WxRichTextCtrl1"));
    
WxRichTextCtrl1->SetFocus();
    
WxRichTextCtrl1->SetInsertionPointEnd();

    
SetTitle(wxT("AutoITEdit"));
    
SetIcon(wxNullIcon);
    
SetSize(8,8,804,523);
    
Center();
    
    
////GUI Items Creation End

Es funktioniert auch soweit , dass ich den Thread erstellen kann und er wird auch gestartet , aber sobald die erste Funktion kommt , für die das Objekt gebraucht wird , beendet sich das Programm ohne irgendwelche Errors oder ähnliches zu hinterlassen .

Edit :
Das ist nicht der komplette Code , aber wenn ich den kompletten Code poste , würde das in Unübersichtlichkeit enden und in Funktionen , die dir nicht bekannt sind
(auser du benutzt wxDevC++).
Dann wird der Thread wohl nicht richtig gestartet. Überprüf einfach mal ob das handle (thread) gültig ist, und falls nicht, lass dir mit den Fehlercode ausgeben.
kennyo is offline  
Thanks
1 User
Old 01/16/2010, 16:27   #8
 
elite*gold: 0
Join Date: Dec 2004
Posts: 697
Received Thanks: 8
Ich habe den Thread zwar nun nur kurz überflogen aber was ich noch gerne bemerken würde zu deinem Test-Programm (.. main() { CreateThread(...); return;})
wenn du den Thread in diesem Programm erzeugst, endet das Programm bevor der Thread möglicherweise ausgeführt wurde. Der Thread ist Teil deines Prozesses und der Prozess endet nach main(). Ein WaitForSingleObject(handle) sollte es da tun.
termi is offline  
Old 01/16/2010, 16:51   #9
 
Shadow992's Avatar
 
elite*gold: 77
Join Date: May 2008
Posts: 5,430
Received Thanks: 5,878
Quote:
Originally Posted by termi View Post
Ich habe den Thread zwar nun nur kurz überflogen aber was ich noch gerne bemerken würde zu deinem Test-Programm (.. main() { CreateThread(...); return;})
wenn du den Thread in diesem Programm erzeugst, endet das Programm bevor der Thread möglicherweise ausgeführt wurde. Der Thread ist Teil deines Prozesses und der Prozess endet nach main(). Ein WaitForSingleObject(handle) sollte es da tun.
Ok danke für die Antwort , aber das war mir klar , Ich wollte den Code nur sehr kurz halten
Shadow992 is offline  
Reply


Similar Threads Similar Threads
Welche Klassen kann man mit den Klassen von WoW vergleichen?
10/09/2011 - General Gaming Discussion - 30 Replies
Thema sagt eigentl. alles ;)
Multithreading
03/17/2010 - AutoIt - 16 Replies
$handle = DllCallbackRegister("_Threadstart"," ;int","ptr") $handlr = DllCallbackRegister("_Threadstard"," ;int","ptr") $dll = DllStructCreate("Char;int") DllStructSetData($dll,1,"hi") DllStructSetData($dll,2,1234) $ret = DllCall("kernel32.dll","hwnd", "CreateThread","ptr",0,"d word",0,"long",DllCallbackGetPtr($h andle),"ptr",DllStructGetPtr($dll)," ;long",0,"int*",0)...
RoM Klassen
05/07/2009 - General Gaming Discussion - 8 Replies
RoM Klassen: 0 Gamemaster 1 Krieger 2 Kundschafter 3 Schurke 4 Magier 5 Priester 6 Ritter 7 Runentänzer
Klassen
03/08/2009 - General Gaming Discussion - 7 Replies
Hi, ich wollte mal fragen, was ich werden kann bzw welch eKlasse ich mich spezialisieren sollte für miene Anforderungen also ich habe ienen prieset / mage 22/17 kein bock, weil ich kein damage mache zwar gut GOT aber das dauert dann nen warrior lvl 21 ist jut geil, weil ich viel damage mache aber ich weiß nicht, was ich 2nd nehmen soll bin am überlegen , ob ich nun mage / priest mach



All times are GMT +1. The time now is 00:14.


Powered by vBulletin®
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Terms of Service | Abuse
Copyright ©2026 elitepvpers All Rights Reserved.