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


145

Как предотвратить двойное включение файлов заголовков? Проблема в том, что я включаюв MyClass.h, а затем я включаю MyClass.h во многие файлы, поэтому он включается несколько раз, и возникает ошибка переопределения. Как предотвратить?

Я использую #pragma once вместо включения охранников, и я думаю, что это нормально.

MyClass.h:

// MyClass.h
#pragma once

#include <winsock2.h>

class MyClass
{

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

РЕДАКТИРОВАТЬ: несколько ошибок, которые я получаю

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 имеют общие структуры. Вы должны включить только один из них, а не оба
Светлозар Ангелов

Ответы:


237

Эта проблема возникает при включении <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 03

Включен ли <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

77

Как предполагали другие, проблема в том, когда 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достаточно для вашего проекта (в большинстве случаев это так). Это, вероятно, приведет к увеличению времени компиляции, но решит любые ошибки / предупреждения.


15
WIN32_LEAN_AND_MEANбыло решением для меня: много танков
Джонатан Клотье

О _WINSOCK_решении: не следует выдавать предупреждение о переопределении макроса, если оба определения идентичны. Распространенная ошибка заключается в том, что люди добавляют определение в проект, не устанавливая никакого значения, и ожидают пустого определения. Однако, если вы добавите -D_WINSOCK_в строку cmd, она будет установлена _WINSOCK_в 1. Чтобы создать пустое определение, -D_WINSOCK_=необходимо передать.
Paweł

Если вы используете #define _WINSOCKAPI_, вам также может понадобиться #define _WINSOCK_DEPRECATED_NO_WARNINGS, в зависимости от ваших обстоятельств.
Lorien Brune

16

Эх - уродство винды ... Здесь важен порядок включения. Вам необходимо включить 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 голоса), но я думаю, что использование include guards - это то же самое, что и прагма один раз, вы поместили их оба?
Светлозар Ангелов

1
Ну, у меня есть #pragma once, который afaik - это тот же заголовок, охраняет
акиф

2
@Angelov: Да, это то, что я говорю, это одно и то же. Проблема не в моих файлах заголовков, но я думаю, что сам <winsock2.h> не имеет защиты заголовков или может быть чем-то еще.
akif 03

1
По определению #pragma зависит от компилятора (нестандартно). Это может работать не на всех компиляторах. Я знаю, что Visual Studio принимает #pargma один раз. Я не уверен, что это делает gcc. Знаю, что включать охрану ВСЕГДА работают. Я использую оба #pragma один раз и включаю охранников для максимальной безопасности. Кажется, что MSVC оптимизировал обработку #pragma once, а gcc оптимизировал обработку включаемых охранников. Единственное отличие от моего стандартного заголовка состоит в том, что #praga однажды находится за пределами защиты включения.
KitsuneYMG 03

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

7

Я столкнулся с этой проблемой, когда пытался вытащить сторонний пакет, который, по-видимому, включал windows.h где-то в его беспорядке заголовков. Определить _WINSOCKAPI_на уровне проекта было намного проще (не говоря уже о более удобном в обслуживании), чем пробираться через их суп и исправлять проблемное включение.


1
В Qt в файле .pro это выглядит так: DEFINES += _WINSOCKAPI_
phyatt

@phyatt: ты должен превратить это в ответ, если нет, я сделаю это!
Лейф Грюнвольдт

@LeifGruenwoldt дерзай! Рад, что смог помочь.
phyatt

6

В VS 2015 будет работать следующее:

#define _WINSOCKAPI_

Пока не будет:

#define WIN32_LEAN_AND_MEAN

6

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

Просто проявите терпение, посмотрите на все по очереди и установите этот порядок, сначала #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

Я всегда использовал postfix guid. Несколько лет назад я столкнулся с очень плохой кодовой базой, в которой были разные файлы заголовков с одинаковым именем и включали защиту. В рассматриваемых файлах был определен класс с таким же именем. Если бы использовались только пространства имен. Некоторые проекты собраны, некоторые нет. Использование уникальных защитных элементов было частью решения по различению заголовков и их содержимого.

В Windows с Visual Studio используйте guidgen.exe, в Linux - uuidgen -t.


4

Я столкнулся с той же проблемой, и вот что я обнаружил до сих пор:

Из этого выходного фрагмента -

c: \ program files \ microsoft sdks \ windows \ v6.0a \ include \ ws2def.h (91): предупреждение C4005: 'AF_IPX': переопределение макроса
c: \ program files \ 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»

Все еще пытаюсь исправить проблему, не внося изменений в код.

Дайте мне знать, если в этом есть смысл.


3

#pragma onceосновывается на полном пути к имени файла. Так что, скорее всего, у вас есть две идентичные копии MyClass.h или Winsock2.h в разных каталогах.


символическая ссылка или пересечение NTFS также могут привести к поломке системы.
Томи

2

Вы должны использовать защиту жатки.

поместите эту строку вверху файла заголовка

#ifndef PATH_FILENAME_H
#define PATH_FILENAME_H

и внизу

#endif

1
#pragma once и включить охранников - это тоже самое, не так ли?
akif 03

Они не совсем то же самое - защита заголовков предотвратит повторное включение файла на уровне препроцессора, к тому же они, очевидно, более переносимы, чем #pragma once.
Timo Geusch 03

1
Я имел в виду, что они созданы для тех же целей :)
akif 03

2
#pragma
Once

1

#pragma onceявляется нестабильным, даже на компиляторах MS, и не поддерживается многими другими компиляторами. Как уже упоминали многие другие люди, использование include guards - лучший способ. Ни в коем случае не используйте #pragma once- это облегчит вам жизнь.


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

3
При использовании #pragma onceкомпилятор принимает имя узла файла заголовка в качестве уникального идентификатора. Это может потерпеть неудачу, если у вас есть символические ссылки или NTFS-соединения в вашем исходном дереве (чаще, чем вы думаете), или даже если у вас есть файл с тем же именем в другом системном каталоге include (это случалось со мной раньше, когда я версия 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.