Файли заголовків C ++ для перегляду (winsock2.h)


143

Як запобігти включенню файлів заголовків двічі? Проблема в тому, що я включаюв MyClass.h, а потім я включаю MyClass.h у багатьох файлах, тому він включає кілька разів і виникає помилка перегляду. Як запобігти?

Я один раз використовую #pragma замість того, щоб включати охоронців, і, мабуть, це добре.

MyClass.h:

// MyClass.h
#pragma once

#include <winsock2.h>

class MyClass
{

// methods
public:
 MyClass(unsigned short port);
 virtual ~MyClass(void);
};

EDIT: Мало помилок, які я отримую

c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(91) : warning C4005: 'AF_IPX' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(460) : see previous definition of 'AF_IPX'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(124) : warning C4005: 'AF_MAX' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(479) : see previous definition of 'AF_MAX'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(163) : warning C4005: 'SO_DONTLINGER' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(402) : see previous definition of 'SO_DONTLINGER'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(206) : error C2011: 'sockaddr' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(485) : see declaration of 'sockaddr'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2143: syntax error : missing '}' before 'constant'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2143: syntax error : missing ';' before 'constant'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2059: syntax error : 'constant'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C2143: syntax error : missing ';' before '}'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(518) : warning C4005: 'IN_CLASSA' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(287) : see previous definition of 'IN_CLASSA'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(524) : warning C4005: 'IN_CLASSB' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(293) : see previous definition of 'IN_CLASSB'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(530) : warning C4005: 'IN_CLASSC' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(299) : see previous definition of 'IN_CLASSC'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(541) : warning C4005: 'INADDR_ANY' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(304) : see previous definition of 'INADDR_ANY'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(543) : warning C4005: 'INADDR_BROADCAST' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(306) : see previous definition of 'INADDR_BROADCAST'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(577) : error C2011: 'sockaddr_in' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(312) : see declaration of 'sockaddr_in'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(132) : error C2011: 'fd_set' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(68) : see declaration of 'fd_set'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(167) : warning C4005: 'FD_SET' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(102) : see previous definition of 'FD_SET'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(176) : error C2011: 'timeval' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(111) : see declaration of 'timeval'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(232) : error C2011: 'hostent' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(167) : see declaration of 'hostent'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(245) : error C2011: 'netent' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(180) : see declaration of 'netent'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(252) : error C2011: 'servent' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(187) : see declaration of 'servent'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(264) : error C2011: 'protoent' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(199) : see declaration of 'protoent'

4
Ви вже використовуєте #pragma один раз, тому його слід включити лише один раз.
Naveen

1
Ваш компілятор не підтримує прагму один раз?
Світлозар Ангелов

Я використовую Visual Studio 2008, чому тоді <winsock2.h> включається двічі?
акіф

1
Він може бути включений двічі з деяких включених заголовків з MyClass.h
Світлозар Ангелов

5
winsock2 і winsock мають спільні структури. Ви повинні включити лише один з них, а не обидва
Світлозар Ангелов

Відповіді:


234

Ця проблема виникає при включенні <windows.h>раніше <winsock2.h>. Спробуйте упорядкувати список включення, який <windows.h>буде включено після <winsock2.h>або визначте _WINSOCKAPI_спочатку:

#define _WINSOCKAPI_    // stops windows.h including winsock.h
#include <windows.h>
// ...
#include "MyClass.h"    // Which includes <winsock2.h>

Дивіться також це .


Я взагалі не включаю <windows.h>, я знаю, <winsock2.h> робить це для мене.
акіф

2
Для мене ваш код компілює нормально, лише <winsock2.h>в MSVC2008. <windows.h>включення змушує генерувати ідентичні помилки компіляції, як ви надали.
pingw33n

Чи включено <windows.h> до stdafx.h?
Колін Десмонд

1
Це рішення вирішило проблему для мене на VS 2010 із SDK 7.1. Дякую pingw33n!
adamfisk

Я мав #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h>порядок і отримував winsock2, h файл не знайдено. Включено #define _WINSOCKAPI_ вище всіх 3 включає ще одну і ту ж помилку
Ava

75

Як запропонували інші, проблема полягає в тому, коли windows.hвона включена раніше WinSock2.h. Тому що windows.hвключає winsock.h. Не можна використовувати і те, WinSock2.hі winsock.h.

Рішення:

  • Включіть WinSock2.hраніше windows.h. Що стосується попередньо складених заголовків, ви повинні вирішити їх там. У випадку простого проекту, це легко. Однак у великих проектах (особливо при написанні переносного коду, без попередньо складених заголовків) це може бути дуже важко, тому що, коли ваш заголовок з WinSock2.hвключено, він windows.hможе бути вже включений з іншого файлу заголовка / реалізації.

  • Визначтесь WIN32_LEAN_AND_MEANперед windows.hпроектом або широко. Але він буде виключати багато інших речей, які вам можуть знадобитися, і ви повинні включити їх самостійно.

  • Визначтесь _WINSOCKAPI_перед windows.hпроектом або широко. Але коли ви включаєте, WinSock2.hви отримуєте попередження про перестановку макросу.

  • Використовуйте windows.hзамість того, WinSock2.hколи winsock.hдостатньо для вашого проекту (у більшості випадків це так). Це, ймовірно, призведе до більш тривалого часу компіляції, але усуне помилки / попередження.


14
WIN32_LEAN_AND_MEANбуло рішенням для мене багато танків
Jonatan Cloutier

Про _WINSOCK_рішення: Вам не слід grt попередження про переосмислення макросу, якщо обидва визначення однакові. Поширена помилка полягає в тому, що люди додають визначення проекту, не встановлюючи значення, і очікують порожнього визначення. Однак, якщо ви додасте -D_WINSOCK_в cmd рядок, він буде встановлений _WINSOCK_на 1. Для створення порожнього визначення -D_WINSOCK_=необхідно пройти.
Paweł Stankowski

Якщо ви користуєтесь #define _WINSOCKAPI_, вам також можуть знадобитися #define _WINSOCK_DEPRECATED_NO_WARNINGS, залежно від ваших обставин.
Лоріен Брун

16

О - потворність Windows ... Тут важливі порядок включень. Потрібно включити winsock2.h перед windows.h. Оскільки windows.h, ймовірно, включений із вашого попередньо складеного заголовка (stdafx.h), вам потрібно буде включити winsock2.h звідти:

#include <winsock2.h>
#include <windows.h>

14

Використовуючи "заголовки заголовка":

#ifndef MYCLASS_H
#define MYCLASS_H

// This is unnecessary, see comments.
//#pragma once

// MyClass.h

#include <winsock2.h>

class MyClass
{

// methods
public:
    MyClass(unsigned short port);
    virtual ~MyClass(void);
};

#endif

2
Я думаю, що я помиляюся (4 оновлення на даний момент), але я думаю, що використовувати включення охоронців - це те саме, що колись прагма, ви поставили їх обох?
Світлозар Ангелов

1
Що ж, у мене є #pragma один раз, який afaik - це той самий охоронець заголовка
akif

2
@Angelov: Так, це я кажу, що це однакові речі. Проблема не в моїх файлах заголовків, але я думаю, що <winsock2.h> сам не має заголовків заголовка або це може бути щось інше.
акіф

1
За визначенням #pragma залежить від компілятора (нестандартно). Це може працювати не у всіх компіляторах. Я знаю, що візуальна студія приймає #pargma один раз. Я не впевнений, чи є GCC. Я знаю, що включати охоронців ВЖЕ завжди працюють. Я використовую обидві #pragma один раз і включаю охорону для максимальної рентабельності. Схоже, що MSVC оптимізував керування #pragma один раз, а gcc оптимізував поводження з охоронцями, що включають. Єдина відмінність від мого стандартного заголовка полягає в тому, що #praga один раз знаходиться поза межами охоронців.
KitsuneYMG

1
Команда '#pragma' визначена в стандарті ANSI для отримання довільного ефекту, визначеного реалізацією. У препроцесорі GNU C "#pragma" вперше намагається запустити гру "шахрай"; якщо це не вдається, він намагається запустити гру «хак»; якщо це не вдасться, він намагається запустити GNU Emacs із відображенням Ханойської вежі; якщо це не вдається, воно повідомляє про фатальну помилку. У будь-якому випадку попередня обробка не триває. - Річард М. Стеллман, препроцесор GNU C, версія 1.34
DevSolar

6

Я зіткнувся з цією проблемою, намагаючись витягнути сторонній пакет, який, мабуть, включав windows.h десь у його безладі заголовків. Визначити _WINSOCKAPI_на рівні проекту було набагато простіше (не кажучи вже про більш ретрансляційному), ніж перебирати їх суп і виправляти проблематичне включення.


1
На Qt, у файлі .pro, це виглядає приблизно так: DEFINES += _WINSOCKAPI_
phyatt

@phyatt: ти повинен перетворити це на відповідь, якщо цього не зробиш, я не буду!
Лейф Грюнвольдт

@LeifGruenwoldt піти на це! Радий, що можу допомогти.
фіат

6

У VS 2015 працюватимуть:

#define _WINSOCKAPI_

Хоча наступне не буде:

#define WIN32_LEAN_AND_MEAN

6

Я перевірив рекурсивний включає, я визначив файли заголовків , які включають в себе (рекурсивно) деякі #include "windows.h"і #include "Winsock.h"і написати #include "Winsock2.h". до цих файлів я додав #include "Winsock2.h"як перше включення.

Просто питання терпіння, погляд на включає в себе один на один і встановити цей порядок, перша #include "Winsock2.h"потім#include "windows.h"


5

Я знайшов це посилання windows.h та winsock2.h, який має альтернативу, яка чудово працювала для мене:

#define _WINSOCKAPI_    // stops windows.h including winsock.h
#include <windows.h>
#include <winsock2.h>

У мене виникли проблеми з пошуком проблеми, але додавши, що #define я зміг створити, не з'ясувавши це.


4

Я б не використовував лише FILENAME_H, але

#ifndef FILENAME_H_AF06570D_B36E_4B82_8F97_C456AF4A38FD
#define FILENAME_H_AF06570D_B36E_4B82_8F97_C456AF4A38FD

//code stuff
#endif // FILENAME_H_AF06570D_B36E_4B82_8F97_C456AF4A38FD

Я завжди використовував настанови про постфікси. Я натрапив на дуже погану базу коду кілька років тому, що мали різні файли заголовків з тим самим ім'ям файлу і містять охорону. Розглянуті файли визначали клас з такою ж назвою. Якби використовувались лише простори імен. Деякі проекти, складені, деякі - не. Використання унікальних гвардійців було частиною рішення в розрізненні заголовків та їх вмісту.

У Windows з Visual Studio використовуйте guidegen.exe, в Linux uuidgen -t.


4

Я зіткнувся з тим же питанням, і ось що я виявив поки що:

З цього вихідного фрагмента -

c: \ програмні файли \ microsoft sdks \ windows \ v6.0a \ включають \ ws2def.h (91): попередження C4005: 'AF_IPX': перевизначення макросу
c: \ програмні файли \ microsoft sdks \ windows \ v6.0a \ include \ winsock.h (460): див. попереднє визначення "AF_IPX"

-Видається, що і ws2def.h, і winsock.h були включені у ваше рішення.

Якщо ви подивитесь на файл ws2def.h, він починається з наступного коментаря -

/*++

Copyright (c) Microsoft Corporation. All rights reserved.

Module Name:

    ws2def.h

Abstract:

    This file contains the core definitions for the Winsock2
    specification that can be used by both user-mode and 
    kernel mode modules.

    This file is included in WINSOCK2.H. User mode applications
    should include WINSOCK2.H rather than including this file
    directly. This file can not be included by a module that also
    includes WINSOCK.H.

Environment:

    user mode or kernel mode

--*/

Зверніть увагу на останній рядок - "Цей файл не може бути включений модулем, який також включає WINSOCK.H"

Проблема все ще намагається усунути, не вносячи змін до коду.

Дайте мені знати, чи це має сенс.


2

Вам слід скористатися захистом заголовка.

помістіть цей рядок у верхній частині файлу заголовка

#ifndef PATH_FILENAME_H
#define PATH_FILENAME_H

і внизу

#endif

1
#pragma один раз і включати охоронців - це те саме, чи не так?
акіф

Вони не зовсім однакові - охоронці заголовків запобігають повторному включенню файлу на рівні препроцесора, плюс вони, очевидно, є більш портативними, ніж один раз #pragma.
Тимо Геуш

1
Я мав на увазі, що вони побудовані для тих же цілей :)
akif

2
#pragma один раз є нестандартним,
afaik

2

#pragma onceзаснований на повному шляху імені файлу. Отже, ви, мабуть, є дві однакові копії або MyClass.h, або Winsock2.h в різних каталогах.


символічне посилання або з'єднання NTFS також спричинить розрив системи.
Томі

1

#pragma onceє flakey, навіть на компіляторах MS, і не підтримується багатьма іншими компіляторами. Як багато інших людей згадували, шляхом використання охоронців включати - це шлях. Не використовуйте #pragma onceзовсім - це значно полегшить ваше життя.


3
На жаль, я бачив, що більше, ніж нульовий збір, включає охоронці, або там, де помилка означає, що захист насправді не працює, або де файли з тим самим іменем у різних каталогах використовують один і той же маркер, або там, де маркер починається з подвійного підкреслити або підкреслити, тоді велика літера (і, отже, не переноситься так само, як #pragma один раз). Отже, що стосується непереносного коду, як-от усього, що використовує winsock.h, мене #pragma одного разу сильно не турбував аж до того моменту, коли ви сказали, що це лускатий. Коли це не вдається, крім того, що взагалі не підтримується?
Стів Джессоп

3
Під час використання #pragma onceкомпілятор приймає ім'я вузла файлу заголовка як унікальний ідентифікатор. Це може вийти з ладу, якщо у вашому дереві джерела є символьні посилання або з'єднання NTFS (більш поширені, ніж ви могли б подумати), або навіть якщо у вас є те саме ім'я в іншому системному каталозі, що включає те саме (це сталося зі мною раніше, коли у мене є версія 1 і версія 2 тієї ж бібліотеки, встановленої до двох різних систем, включають шляхи). Підсумок: для мене я вважаю за краще мати більше контролю і жити з випадковою помилкою програмного забезпечення, а не довіряти компілятору, щоб зробити це за мене.
Томі


1

У моєму проекті (я використовую VS 2008 SP1) працює наступне рішення:

Файл заголовка:

//myclass.h
#pragma once
#define _WINSOCKAPI_
#include <windows.h>

Клас Cpp:

//myclass.cpp
#include "Util.h"
#include "winsock2class.h"
#pragma comment(lib, "Ws2_32.lib")

де #include "winsock2class.h" середній клас, який реалізував winsock2.h:

//winsock2class.h
#include <winsock2.h>
#include <windows.h>
#pragma comment(lib, "Ws2_32.lib")

0

Я фактично зіткнувся з проблемою, в якій мені довелося визначити winsock2.h як перше, але, мабуть, є й інші проблеми з включеннями з інших пакетів. Сподіваюся, це корисно тому, хто стикається з тією ж проблемою, не лише Windows.h, але все включає.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.