C++ in C einbinden
-
Hallo,
ich habe zwei Programmteile, einen C-Teil der zwingend mit einen C-Compiler kompiliert werden werden muss und einen C++-Teil der mit einem C++-Compilier kompiliert wird. Erschwerend kommt hinzu, dass der C-Teil auf Elemente des C++-Teils zugreifen können muss bzw. auch umgekehrt der C++-Teil auf den C-Teil.
Nachdem ich etwas recherchiert habe schien es mir plausibel, die Header-Files generell nach folgendem Template (Beispiel.h) aufzubauen und in einen C bzw. C++ Teil zu unterteilen, so das beide Compiler mit den Header-Files zurechtkommen.
/* * Beispiel.h * */ #ifndef TASK1_H_ #define TASK1_H_ // ---------------------------------------------- /* * C++ header files */ // ---------------------------------------------- #ifdef __cplusplus extern "C" { #endif /* * C header files */ #ifdef __cplusplus } #endif // ---------------------------------------------- #ifdef __cplusplus /* * C++ src code */ #endif // ---------------------------------------------- #ifdef __cplusplus extern "C" { #endif /* * exported c wrapper functions * */ #ifdef __cplusplus } #endif // -------------------------------------------- #endif /* TASK1_H_ */
Jetzt bin ich aber in dem Dilemma das ich z.B. C++-Header-Files einbinden muss, die sich nicht an meine "Konvention" halten. D.h. wenn ich eine solche Header-Datei in meinem Header-Template einbinde, dann kommt der C-Compiler (gcc) mit dem C++-Code im nicht konventionsgemäßen Header-File nicht klar
Beispiel:
#include <Beispiel.h> int main( void ) { .... }
Bindet die nach meinem Template erstellte Beispiel.h (s. oben) eine weitere C++-Header-Datei ein, die nicht nach diesem Template erstellt wurde, bekomme ich beim Kompilieren z.B. solche Fehlermeldungen:
In file included from ./Task.h:22:0, from main.c:71: ../TaskCommon.h:41:1: error: unknown type name 'namespace' ../TaskCommon.h:41:19: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token In file included from ../Fixed.h:50:0, from ../TaskCommon.h:74, from ./Task.h:22, from main.c:71: ../Signals.h:33:18: fatal error: string: No such file or directory
In der obigen Fehlerausgabe sind die Header-Dateien "TaskCommon.h" und "Signals.h" nicht nach meinem Template aufgebaut.
Hat jmd. einen Vorschlag wie ich dieses Problem fixen könnte? Ich kann ja schlecht erwarten, dass sich alle an meine Konvention halten
-
Du packst zu viel in deine Header. Mach einen Header, der nur die Schnittstelle enthält. Diesen baust du dann nach dem Muster:
#ifndef FOO_H #define FOO_H // Weitere, benötigte Header #ifdef __cplusplus extern "C" { #endif // Schnittstellenfunktionen zur Benutzung in C #ifdef __cplusplus } #endif
Unter die "weiteren benötigten Header" können naturgemäß nur Header fallen, die zu C und C++ kompatibel sind, sonst ist die Schnittstelle nicht wohldefiniert. Also in der Regel irgendwelche struct-Definitionen und typedefs, wenn diese aus irgendeinem Grund nicht direkt im Schnittstellenheader stehen sollen.
-
Abgesehen davon dass ich u.U. zuviel in den Header gepackt bleibt mir keine andere Möglichkeit als nur Header-Dateien die C und C++ kompatibel sind einzubinden?
Ich komme im Moment nicht drumherum den "nativen" C++-Header von TaskCommon.h einzubinden. Das würde nach meinem Verständnis bedeuten, dass ich TaskCommon.h erst C und C++ kompatibel machen muss bevor ich ihn einbinden kann?
-
Was ist denn der Grund dafür, dass Du Deine C-Programme nicht mit einem C++ - Compiler übersetzen kannst?
-
nicnoc schrieb:
Ich komme im Moment nicht drumherum den "nativen" C++-Header von TaskCommon.h einzubinden.
Das kann nicht sein, dann würde in der Schnittstelle irgendetwas aus TaskCommon.h benutzt werden. Wenn das aber so ist, dann ist die Schnittstelle nicht C-kompatibel und daher liegt der Fehler im Design der Schnittstelle.
Belli schrieb:
Was ist denn der Grund dafür, dass Du Deine C-Programme nicht mit einem C++ - Compiler übersetzen kannst?
Vermutlich weil - entgegen anderslautender Gerüchte - C eben doch nicht wirklich kompatibel zu C++ ist. Zumindest, wenn man etwas über "Hello World" hinaus geht. Größter Missetäter dürfte wohl der Unterschied in der impliziten Konvertierung zwischen Pointertypen sein.
-
Der Grund ist, dass ich versuche eine C++ Applikation für FreeRTOS (http://www.freertos.org/) zu schreiben. Dabei muss nach meinem Kenntnisstand das Betriebssystem mit einem C-Compiler erstellt werden.
I know the data hiding in the FreeRTOS source prevents it linking completely as C++, so generally people either have to compile the FreeRTOS source as C and their own application as C++ (note 1: the extern "C" code is already present in the header files. note 2: taking note of richard_damon's comments above) or alternatively move the currently private FreeRTOS data structure definitions into the public header files.
Die eigentlich zu entwickelnde Applikation (Mikrocontroller, NXP LPC1768) muss aber in C++ entwickelt werden bzw. wurde bereits ein Teil der Anwendung auf der x86-Architektur in C++ entwickelt und läuft dort auch zufriedenstellenden.
Mein Part ist es nun, auf die vorhandenen C++ Klasse zurückzugreifen und weitere Dinge mit nativen (C-)-FreeRTOS-Funktionen zu realisieren. Ich bin jetzt auch nicht der C/C++ Entwickler vor dem Herren, aber ich muss mich da jetzt irgendwie durchwursteln
-
Größter Missetäter dürfte wohl der Unterschied in der impliziten Konvertierung zwischen Pointertypen sein.
Exakt!
Das kann nicht sein, dann würde in der Schnittstelle irgendetwas aus TaskCommon.h benutzt werden. Wenn das aber so ist, dann ist die Schnittstelle nicht C-kompatibel und daher liegt der Fehler im Design der Schnittstelle.
Mmh, ich bin mir nicht ganz sicher ob ich Dich richtig verstehe. Nehmen wir mal als Beispiel einen Task der Applikation.
Es gibt eine allgemein gehaltene "native" C++-Klasse "TaskCommon" mit einem zugehörigen Header usw. Ich leite jetzt von dieser Klasse ab und bilde eine spezialisierte "Task"-Klasse.
Das Beispiel hinkt etwas, aber würde ich diese Klasse auf der C-Seite einbinden, würde die "Task"-Klasse angenommen werden, während mir die "TaskCommon"-Klasse um die Ohren fliegt.
/* * Task.h * * Created on: 08.02.2014 */ #ifndef TASK_H_ #define TASK_H_ /* * * v = void * ux = unsigned <irgendwas> * x = (signed) <irgendwas> * */ #include <LPC1768Function.h> #include <string.h> #include <TaskCommon.h> #include <TaskList.h> #ifdef __cplusplus extern "C" { #endif #include <FreeRTOS.h> #include <task.h> #ifdef __cplusplus } #endif #ifdef __cplusplus namespace pearlrt { class Task : public TaskCommon { public: xTaskHandle xth; char tskName[10]; unsigned portSHORT stackDepth; unsigned portBASE_TYPE priority; portBASE_TYPE convertToFreeRTOSPriority(int pearlPrio); Task(char const* name, unsigned portBASE_TYPE priority, unsigned portSHORT stackDepth); Task(); /* void activate(char* taskname, unsigned portBASE_TYPE priority); void activateWithDefaultPriority(char* tskname); void terminate(char* taskname); void supend(char* taskname); void cont(char* taskname); */ void printDebugMessage(char* message); void resume(int condition, Clock at = 0.0, Duration after = 0.0, Interrupt when = Interrupt()); void prevent(); bool isMySelf(); void directActivate(const Fixed<15>& prio); void scheduledActivate(int condition,const Fixed<15>& p, const Clock& at, const Duration& after, const Duration& all, const Clock& until, const Duration& during, const Interrupt& when); void terminateMySelf(); void terminateFromOtherTask(); void suspendMySelf(); void suspendFromOtherTask(); void scheduledContinue(const int condition, const Fixed<15>& p, const Clock& at, const Duration& after, const Duration & all, const Clock& until, const Duration& during, const Interrupt &when); void continueMySelf(const Fixed<15>& prio); void continueFromOtherTask(Fixed<15> prio = (Fixed<15>)0); virtual void task() = 0; /* call task job*/ static void tskfunc(void* parm); }; } #endif // ----------------------------------------------------------------------------- #ifdef __cplusplus extern "C" { #endif /* * Exportierte Task Wrapper Funktionen */ #ifdef __cplusplus } #endif // ----------------------------------------------------------------------------- #endif /* TASK_H_ */
/* [The "BSD license"] Copyright (c) 2012-2013 Rainer Mueller All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TASKCOMMON_INCLUDED #define TASKCOMMON_INCLUDED /** \file \brief tasking functionality interface towards the compiler */ namespace pearlrt { class TaskCommon; class Semaphore; /** reasons for blocking a task */ enum BlockReason { NOTBLOCKED, ///< task is currently not blocked REQUEST ///< task is block due to request }; /** data structure for blocking request */ struct BlockData { BlockReason reason; ///< reason for blocking /** since a task may be blocked only by one reason - the parameters of the blocking request may be stored in a union */ union BlockReasons { /** blocking due to REQUEST operation */ struct BlockSema { int nsemas; ///< number of semaphores in the REQUEST call Semaphore **semas; ///< pointer to the array of semaphores } sema; ///< \returns the semaphore component of blocking } u; ///< \returns the union containing all blocking requests }; } #include "Fixed.h" #include "Clock.h" #include "Duration.h" #include "Interrupt.h" #include "Prio.h" #include "Mutex.h" #include "CSema.h" namespace pearlrt { /** \brief interface to the task object of the indidivual implementation This class provides all necessary operations for the compiler to realize the tasking functionality required by the PEARL language. The common features are <ul> <li>a mutex to enshure atomic opration on the task control data <li>priority (defualt and current) <li>the tasks name <li>the main attribute <li>the tasks state <li>the tasks location (file+line) </ul> The implementation must provide C-macros named DCLTASK(tname, prio, ismain) SPCTASK(tname) The macro DCLTASK must be written in that way that the tasks body code may follow immediatelly (with the leading opening brace supplied by the compiler). The parameters are: tname: The name of the task prio: the priority of the task ismain: 1, if the task shall be started automatically; 0, else \note The type REF TASK should be handled inside the compiler To realize blocking due to semaphore, bolt or i/o-operations the operations block() and unblock() are provided. The task state diagram shows the possibilities to change the state. Regard that the state running includes the internal state runnable. <ul> <li>State terminated<br> A task is waiting to become activated.</li> <li>State running<br> The task is ready for execution or executing, depending on the cpu situation. The scheduler performs the switching between these two states automatically. <li>State suspended<br> The task's execution is halted, but may be continued without loss of information. The task does not try to allocate cpu time. Allocated system ressources remain allocated. <li>States blockedSema<br> The task cannot continue operation, because it waits for a semaphore. <li>State blockedIO<br> The task cannot continue operation, because it waits for completion of an i/o-operation. <li>suspendedSema<br> The task was suspended while waiting for a semaphore. After continuation, the task waits again for the semaphore. <li>suspendedIO<br> The task was suspended while waiting for completion of an i/o-operation. The transition to suspendedIO is done at a suitable point of execution. E.q. after an end of record, or after an data element. After continuation the i/o-operation continues at the suspended position. </ul> \image html taskstates.jpg \image latex taskstates.eps */ class TaskCommon { private: /** tests for correct combination AT/ALL/.. */ void testScheduleCondition(int condition, Duration during, Duration all); public: /** possible states of a thread */ enum TaskState { TERMINATED, //< task is terminated RUNNING, //< task is running (runnable) SUSPENDED, //< task is suspended SEMA_SUSPENDED_BLOCKED, //< task is suspended and (prev.) sema op. SEMA_BLOCKED, //< task is blocked due to sema request IO_SUSPENDED_BLOCKED, //< task is suspended and (prev.) dation op. IO_BLOCKED //< task is blocked due to dation operation }; /** task scheduling parameters may be combined in many combinations This enumeration provides the possibility by 'or'ing the different values as requested from the user program. */ enum TaskScheduling { AT = 1, AFTER = 2, WHEN = 4, ALL = 8, DURING = 16, UNTIL = 32, PRIO = 64 }; protected: Mutex mutexTask; ///< Mutex for task object Fixed<15> defaultPrio; ///< PEARL priority from TASK statement Fixed<15> currentPrio; ///< current priority, may be differnt from char * name; ///< PEARL task name int isMain; ///< MAIN attribute of the PEARL task enum TaskState taskState; ///< state of the thread /* backtrace information for signal handler */ int sourceLine; ///< current source file line number const char * fileName; ///< current source file name /** data to perform task blocking/unblocking requests */ struct BlockParams { CSema semaBlock; ///< Semaphor for blocking operation /** reference to next task which waits on this semaphore*/ TaskCommon * next; /** reason on blocking */ BlockData why; } blockParams; ///< control block to store the blocking request public: /** suspend a task If the task is in RUNNING state, the task will no longer aquired any ressources from the system. Ressources, which are assigned to the task will remain assigned to the task. If the task is in BLOCKED state, the task will no longer try to receive the unblocking condition. After continuation, the task will try to get the unblocking condition. (see RTOS-UH p210 21-06-2006) If the task is any other state, no operation is performed. */ void suspend(); /** continue a task If a condition is specified, a timer is armed. The timer triggers a signal handler, which performs the continue-actions as scheduled with the given parameters. \param prio the new PEARL priority of the task, if given <br> if not given, the current priority remains unchanged \param condition pattern of parameters for scheduled activation (see enum TaskScheduling) \param at the time the task should be continued. In case the current time is larger than the given time, the action will occur the next day \param after the duration which must pass before the action occurs \param all the duration between two continuations \param until the time which defines the last continuation of the task. In case the current time is larger than the given time it will be interpreted as the next day \param during the time while the actions may occur \param when the (external) interrupt which starts the schedule \throws InternalTaskSignal if the required system ressources are unavalibale */ void cont(int condition = 0, Prio prio = Prio(), Clock at = 0.0, Duration after = 0.0, Duration all = 0.0, Clock until = 0.0, Duration during = 0.0, Interrupt when = Interrupt()); /** Activate a task perform all required internal operations before starting a new posix thread \param condition pattern of parameters for scheduled activation \param prio the new PEARL priority of the task, if given (see enum TaskScheduling) \param at the time the task should start. In case the current time is larger than the given time, the activation will occur the next day \param after the duration which must pass befor activation occurs \param all the duration between two activations \param until the time which defines the last activation of the task. In case the current time is larger than the given time it will be interpreted as the next day \param during the time while the activations may occur \param when the (external) interrupt which start the schedule \throws InternalTaskSignal if the required system ressources are unavalibale \throws TaskRunningSignal if the tasks thread is already active */ void activate(int condition = 0, Prio prio = Prio(), Clock at = 0.0, Duration after = 0.0, Duration all = 0.0, Clock until = 0.0, Duration during = 0.0, Interrupt when = Interrupt()); /** Terminate a task In case of terminating a blocked task, the blocking condition is skipped, without modifications of e.g. the semaphore. */ void terminate(); /** Perform a scheduled RESUME There is currently no action with is plattform independend. Therefore this method is pur virtual. \param condition a bit map with the resume condition (see enum TaskScheduling) \param at the time for the continuation \param after the requested delay befor continuation \param when the (external) Interrupt which should be awaited */ virtual void resume(int condition, Clock at = 0.0, Duration after = 0.0, Interrupt when = Interrupt()) = 0; /** remove all scheduled action for the task There is currently no action with is plattform independend. Therefore this method is pur virtual. <ul> <li>reset all internal scheduling parameters to the reset state <li>stop timer </ul> */ virtual void prevent() = 0; /** deliver the name of the task \returns name of the task as C-string */ char * getName(); /** check if the task should be started automatically at system start \returns 0, if no<br>1 if yes */ int getIsMain(); /** get the current task state \return the task state */ TaskState getTaskState(); /** retrieve the tasks priority delivers the current PEARL priority \returns the PEARL priority of the task */ Fixed<15> getPrio(); /** set source location set location of the current PEARL statement \param lineNumber source line number \param fileName pointer to a C-string containing the source code */ void setLocation(int lineNumber, const char * fileName); /** get current source file \returns C-string with the last set location file */ const char* getLocationFile(); /** get current source line \returns C-string with the last set location file */ int getLocationLine(); /** block a task due to sempahore, bolt or other operation \param why the blocking reason */ void block(BlockData * why); /** unblock a task due to sempahore, bolt or other operation */ void unblock(); /** obtain blocking request \param why gets a copy of the blocking reason */ void getBlockingRequest(BlockData *why); /** get link to next blocked task (waiting for the same ressource) \returns pointer to TCB of next task in list */ TaskCommon* getNext(); /** set link to next blocked task (waiting for the same ressource) \param t pointer to TCB of next task in the wait queue */ void setNext(TaskCommon*t); /* ---------------------------------------------------- */ /* methods to be provided by specialization */ /* ---------------------------------------------------- */ /** check if the calling task is the same as THIS task The behavior for some tasling methos is different if they are used for the own task or for another task (e.q. SUSPEND) \returns true, if the calling task is the task of this object */ virtual bool isMySelf() = 0; /** activate a task immediatelly \note the method may throw exceptions in case of execution problems \param prio the new priority in PEARL semantics; this value is ether the default value, or a new value as specified */ virtual void directActivate(const Fixed<15>& prio) = 0; /** schedule activation a task perform all required internal operations to setup e.g. timers to activate the task at the specified events \note the method may throw exceptions in case of execution problems \param p the new priority in PEARL semantics; this value is ether the default value, or a new value as specified \param condition pattern of parameters for scheduled activation (see enum TaskScheduling) \param at the time the task should start. In case the current time is larger than the given time, the activation will occur the next day \param after the duration which must pass befor activation occurs \param all the duration between two activations \param until the time which defines the last activation of the task. In case the current time is larger than the given time it will be interpreted as the next day \param during the time while the activations may occur \param when the (external) interrupt which start the schedule \throws InternalTaskSignal if the required system ressources are unavalibale \throws TaskRunningSignal if the tasks thread is already active */ virtual void scheduledActivate(int condition, const Fixed<15>& p, const Clock& at, const Duration& after, const Duration& all, const Clock& until, const Duration& during, const Interrupt& when) = 0; /** self termination on the task the method never returns \note the mutexTask must be unlocked as last operation */ virtual void terminateMySelf() = 0; /** termination of the task by another task This method returns when the other task is terminated. \note The method must deal with the current task states! \note the mutexTask is locked */ virtual void terminateFromOtherTask() = 0; /** self suspending of the task the method returns after the corresponding continue was done \note the mutexTask must be unlocked as last operation */ virtual void suspendMySelf() = 0; /** suspending the task by another task This method returns when the other task is suspended. \note The method must deal with the current task states! \note the mutexTask is locked */ virtual void suspendFromOtherTask() = 0; /** scheduled continueation of a task perform all required internal operations to setup e.g. timers to continue the task at the specified events \note the method may throw exceptions in case of execution problems \param p the new priority in PEARL semantics; this value is ether the default value, or a new value as specified \param condition pattern of parameters for scheduled activation (see enum TaskScheduling) \param at the time the task should start. In case the current time is larger than the given time, the activation will occur the next day \param after the duration which must pass befor activation occurs \param all the duration between two activations \param until the time which defines the last activation of the task. In case the current time is larger than the given time it will be interpreted as the next day \param during the time while the activations may occur \param when the (external) interrupt which start the schedule \throws InternalTaskSignal if the required system ressources are unavalibale \throws TaskRunningSignal if the tasks thread is already active */ virtual void scheduledContinue(const int condition, const Fixed<15>& p, const Clock& at, const Duration& after, const Duration & all, const Clock& until, const Duration& during, const Interrupt &when) = 0; /** self continuation of the task The method returns after setting the new priority. After setting the priority this method is due to update the attribute currentPrio \param prio the new PEARL priority of the task */ virtual void continueMySelf(const Fixed<15>& prio) = 0; /** continuation of the task by another task \param prio the new priority of the task, if given \note The method must deal with the current task states! \note the mutexTask is locked */ virtual void continueFromOtherTask(Fixed<15> prio = (Fixed<15>)0) = 0; }; } #endif
-
Kannst du entweder konkret werden oder - noch besser - ein repräsentatives Beispiel für dein Problem geben? Wie schon gesagt, kann es eigentlich nicht sein, dass du in der Schnittstelle einen C++-Header brauchst.
-
Wie gesagt solange ich alle Header-Dateien in mein Template packe ist alles gut. Sobald ich aber eine Header-Datei einbinde die nicht der Konvention entspricht fliegt mir alles um die Ohren.
Ich wüsste aber auch nicht wie man die Schnittstellen besser definieren könnte. Ich komme ja nicht drumherum von "TaskCommon" abzuleiten und die Klasse "Task" (je nach Realisierung) direkt oder indirekt über eine weitere Header-Datei im C-Code über ein #include bekannt zu machen. Die "Task"-Klasse referenziert dann wieder "TaskCommon" usw.
Unterm Strich werde ich vermutlich immer eine Header-Datei haben die selbst bzw. indirekt wiederum eine Header-Datei einbindet die auf C++-Code referenziert und das frisst dann der gcc nicht.
-
nicnoc schrieb:
Ich komme ja nicht drumherum von "TaskCommon" abzuleiten und die Klasse "Task" (je nach Realisierung) direkt oder indirekt über eine weitere Header-Datei im C-Code über ein #include bekannt zu machen
Eine Klasse im C-Code? Wie soll das denn gehen?
-
manni66 schrieb:
nicnoc schrieb:
Ich komme ja nicht drumherum von "TaskCommon" abzuleiten und die Klasse "Task" (je nach Realisierung) direkt oder indirekt über eine weitere Header-Datei im C-Code über ein #include bekannt zu machen
Eine Klasse im C-Code? Wie soll das denn gehen?
+1
Wieso muss die Klasse in der Task.h definiert werden? Wie ich schon sagte, da gehört nur die Schnittstelle rein. Von der Schnittstelle selber ist hingegen nichts zu sehen. Die kann auch keine Klassen beinhalten.
-
Du könntest das zum Beispiel so machen:
C-Header:struct task_handle; task_handle create_task_handle(); void destroy_task_handle(task_handle*); void do_task(task_handle*);
C-Header Implementierung
// include C-Header // include task-Heaader struct task_handle { task *ptr_; }; task_handle* create_task_handle() { auto handle = new task_handle; handle->ptr_ = new concrete_task(...); return handle; } void destroy_task_handle(task_handle* handle) { delete handle->ptr_; delete handle; } void do_task(task_handle* handle) { handle->task(); }