├── .gitignore ├── LICENSE ├── README.md ├── Setup.hs ├── ext_lib ├── Makefile ├── capi.cpp ├── capi.h ├── lib.cpp └── lib.h ├── haskell-and-cpp.cabal ├── src └── Sample.hs └── test └── Main.hs /.gitignore: -------------------------------------------------------------------------------- 1 | ### Haskell ### 2 | dist 3 | cabal-dev 4 | *.o 5 | *.hi 6 | *.chi 7 | *.chs.h 8 | .virthualenv 9 | .hsenv 10 | .cabal-sandbox/ 11 | cabal.sandbox.config 12 | cabal.config -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Jakub Fijałkowski 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Haskell and C++ 2 | --------------- 3 | 4 | This is source code for a blog post on [my page](http://www.codinginfinity.me/post/2015-04-18/haskell_and_cpp "Coding Infinity"). Check it out for more information. -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Data.Maybe 2 | import Distribution.PackageDescription hiding (Flag) 3 | import Distribution.Simple 4 | import Distribution.Simple.LocalBuildInfo 5 | import Distribution.Simple.Setup 6 | import Distribution.Simple.Utils 7 | import System.Directory 8 | 9 | main = defaultMainWithHooks simpleUserHooks 10 | { 11 | preConf = makeExtLib 12 | , confHook = \a f -> confHook simpleUserHooks a f >>= updateExtraLibDirs 13 | , postCopy = copyExtLib 14 | , postClean = cleanExtLib 15 | } 16 | 17 | makeExtLib :: Args -> ConfigFlags -> IO HookedBuildInfo 18 | makeExtLib _ flags = do 19 | let verbosity = fromFlag $ configVerbosity flags 20 | rawSystemExit verbosity "env" 21 | ["make", "--directory=ext_lib"] 22 | return emptyHookedBuildInfo 23 | 24 | updateExtraLibDirs :: LocalBuildInfo -> IO LocalBuildInfo 25 | updateExtraLibDirs localBuildInfo = do 26 | let packageDescription = localPkgDescr localBuildInfo 27 | lib = fromJust $ library packageDescription 28 | libBuild = libBuildInfo lib 29 | dir <- getCurrentDirectory 30 | return localBuildInfo { 31 | localPkgDescr = packageDescription { 32 | library = Just $ lib { 33 | libBuildInfo = libBuild { 34 | extraLibDirs = (dir ++ "/ext_lib/lib") : 35 | extraLibDirs libBuild 36 | } 37 | } 38 | } 39 | } 40 | 41 | copyExtLib :: Args -> CopyFlags -> PackageDescription -> LocalBuildInfo -> IO () 42 | copyExtLib _ flags pkg_descr lbi = do 43 | let libPref = libdir . absoluteInstallDirs pkg_descr lbi 44 | . fromFlag . copyDest 45 | $ flags 46 | let verbosity = fromFlag $ copyVerbosity flags 47 | rawSystemExit verbosity "cp" ["ext_lib/lib/libext.a", libPref] 48 | 49 | cleanExtLib :: Args -> CleanFlags -> PackageDescription -> () -> IO () 50 | cleanExtLib _ flags _ _ = 51 | let verbosity = fromFlag $ cleanVerbosity flags 52 | in rawSystemExit verbosity "env" ["make", "--directory=ext_lib", "clean"] -------------------------------------------------------------------------------- /ext_lib/Makefile: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | AR ?= ar 3 | CXXFLAGS ?= -Wall -std=c++11 -fPIC 4 | 5 | all: lib/libext.a 6 | 7 | lib: 8 | mkdir lib 9 | 10 | %.o: %.cpp 11 | $(CXX) $(CXXFLAGS) -c -o $@ $< 12 | 13 | lib/libext.a: lib lib.o capi.o 14 | $(AR) rcvs $@ lib.o capi.o 15 | 16 | clean: 17 | rm -rf lib 18 | rm -f lib.o 19 | rm -f capi.o 20 | 21 | .PHONY: all clean 22 | -------------------------------------------------------------------------------- /ext_lib/capi.cpp: -------------------------------------------------------------------------------- 1 | #include "capi.h" 2 | #include "lib.h" 3 | 4 | int c_randomNumber() 5 | { 6 | auto ptr = new MyFancyClass(); 7 | auto result = ptr->randomNumber(); 8 | delete ptr; 9 | return result; 10 | } -------------------------------------------------------------------------------- /ext_lib/capi.h: -------------------------------------------------------------------------------- 1 | #ifndef __CAPI_H__ 2 | #define __CAPI_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" int c_randomNumber(); 6 | #else 7 | int c_randomNumber(); 8 | #endif 9 | 10 | #endif -------------------------------------------------------------------------------- /ext_lib/lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "lib.h" 3 | 4 | int MyFancyClass::randomNumber() 5 | { 6 | std::cout << "C++ method called!" << std::endl; 7 | return 785; 8 | } -------------------------------------------------------------------------------- /ext_lib/lib.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIB_H__ 2 | #define __LIB_H__ 3 | 4 | class MyFancyClass 5 | { 6 | public: 7 | int randomNumber(); 8 | }; 9 | 10 | #endif -------------------------------------------------------------------------------- /haskell-and-cpp.cabal: -------------------------------------------------------------------------------- 1 | name: haskell-and-cpp 2 | version: 0.1 3 | license: MIT 4 | license-file: LICENSE 5 | build-type: Custom 6 | cabal-version: >=1.10 7 | 8 | extra-source-files: 9 | ext_lib/*.cpp 10 | ext_lib/*.c 11 | ext_lib/Makefile 12 | 13 | library 14 | exposed-modules: Sample 15 | build-depends: base >= 4.7 && < 5 16 | hs-source-dirs: src 17 | default-language: Haskell2010 18 | extra-libraries: ext, stdc++ 19 | 20 | test-suite test 21 | hs-source-dirs: test 22 | main-is: Main.hs 23 | type: exitcode-stdio-1.0 24 | default-language: Haskell2010 25 | build-depends: 26 | base >= 4.7 && < 5 27 | , hspec >= 2.1.5 28 | , haskell-and-cpp 29 | -------------------------------------------------------------------------------- /src/Sample.hs: -------------------------------------------------------------------------------- 1 | module Sample where 2 | 3 | import Foreign.C 4 | 5 | foreign import ccall "capi.h" c_randomNumber :: IO CInt -------------------------------------------------------------------------------- /test/Main.hs: -------------------------------------------------------------------------------- 1 | import Sample 2 | import Test.Hspec 3 | 4 | main :: IO () 5 | main = hspec $ do 6 | describe "sample_function" $ do 7 | it "should return 785" $ do 8 | c_randomNumber `shouldReturn` 785 9 | --------------------------------------------------------------------------------