erlang本身提供一个接口,可以用来检查模块是否有导出函数,这个接口是erlang:function_exported/3,但是很多时候这个接口无法正常使用。
下面重现一下这个问题:
  1. 1> erlang:function_exported(crypto,start,0).
  2. false
  3. 2> crypto:start().
  4. ok
  5. 3> erlang:function_exported(crypto,start,0).
  6. true
注意:例子中并不是说一定要crypto:start()才能使用这个函数,只是说这个函数确实存在。
 
现在,来看下erlang对这个接口的说明:
Returns true if the module Module is loaded and contains an exported function Function/Arity; otherwise false.
Returns false for any BIF (functions implemented in C rather than in Erlang).
换句话说,如果一个模块还没有被加载,就无法使用erlang:function_exported/3函数。很多模块在erlang启动时都没有加载到系统,都是在使用到的时候才加载,所以这个检查导出函数的接口可能会出现错误的结果。
 
如果要判断模块是否有导出函数,那么,我们可以像下面这么写,就可以正常使用了。
  1. -module(test).
  2. -compile(export_all).
  3. function_exported(Module, Function, Arity) ->
  4. case erlang:module_loaded(Module) of
  5. true ->
  6. next;
  7. _ ->
  8. code:ensure_loaded(Module)
  9. end,
  10. erlang:function_exported(Module, Function, Arity).

另外,如果模块被改成bif也无法判断了。不过这倒不用太过担心,我们自己写的函数都不会是bif

最后,讨论下erlang:function_exported/3为何只能检查已加载过的模块?
为了实现热更新,新旧代码替换,erlang维护了一个全局哈希表,用于描述模块导出函数信息。那么,只要加载过的模块就会被记录信息到这个哈希表,保存这些信息也要耗费一定的内存开销,而且,哈希算法本身存在冲突的可能性,元素越多,发生冲突的可能性越大,为了解决冲突,还会引入了bucket(哈希桶)或者链表。这样,如果要保证键值分散,就会浪费很多空间,不然又会影响查找效率。
所以,erlang只记录了加载过的模块信息,其他等到使用到的时候再加载。
erlang:function_exported/3是一个bif函数,实现函数可以在erts\emulator\beam\bif.c找到:
  1. BIF_RETTYPE function_exported_3(BIF_ALIST_3)
  2. {
  3. if (is_not_atom(BIF_ARG_1) || is_not_atom(BIF_ARG_2) ||  is_not_small(BIF_ARG_3)) {
  4. BIF_ERROR(BIF_P, BADARG);
  5. }
  6. if (erts_find_function(BIF_ARG_1, BIF_ARG_2, signed_val(BIF_ARG_3),
  7. erts_active_code_ix()) == NULL) {
  8. BIF_RET(am_false);
  9. }
  10. BIF_RET(am_true);
  11. }

erts_find_function实现在erts\emulator\beam\export.c中:

  1. /*
  2. * Find the export entry for a loaded function.
  3. * Returns a NULL pointer if the given function is not loaded, or
  4. * a pointer to the export entry.
  5. *
  6. * Note: This function never returns export entries for BIFs
  7. * or functions which are not yet loaded.  This makes it suitable
  8. * for use by the erlang:function_exported/3 BIF or whenever you
  9. * cannot depend on the error_handler.
  10. */
  11. Export* erts_find_function(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix)
  12. {
  13. struct export_templ templ;
  14. struct export_entry* ee;
  15. ee = hash_get(&export_tables[code_ix].htable, init_template(&templ, m, f, a));
  16. if (ee == NULL ||   (ee->ep->addressv[code_ix] == ee->ep->code+3 &&
  17. ee->ep->code[3] != (BeamInstr) BeamOp(op_i_generic_breakpoint))) {
  18. return NULL;
  19. }
  20. return ee->ep;
  21. }

这里,export_tables是一个全局变量,还是在export.c,其中,code_ix被用来控制代码版本,有时间的话再讨论erlang热更新机制

  1. static IndexTable export_tables[ERTS_NUM_CODE_IX];  /* Active not locked */

所以,erlang:function_exported/3只是去查找导出函数的哈希表,找到返回true,没有就false

 
参考:http://blog.csdn.net/mycwq/article/details/40663737

erlang判断模块导出函数问题的更多相关文章

  1. DLL模块例1:使用.def模块导出函数,规范修饰名称,显示连接调用dll中函数

    以下内容,我看了多篇文章,整合在一起,写的一个例子,关于dll工程的创建,请参考博客里另一篇文章:http://www.cnblogs.com/pingge/articles/3153571.html ...

  2. 关于DLL模块导出函数

    当然以前我知道有一个.def文件的,里面写的都是需要导出的函数,以为与__declspec(dllexport)作用是一样的.但是今天看公司项目源码的时候才知道,它们两个导出方法是有一定的区别的,编译 ...

  3. driver: linux2.6 内核模块导出函数实例(EXPORT_SYMBOL) 【转】

    转自:http://blog.chinaunix.net/uid-23381466-id-3837650.html 内核版本:2.6.38-11-generic 内核自己都大量利用内核符号表导出函数, ...

  4. python inspect 模块 和 types 模块 判断是否是方法,模块,函数等内置特殊属性

    python inspect 模块 和 types 模块 判断是否是方法,模块,函数等内置特殊属性 inspect import inspect def fun(): pass inspect.ism ...

  5. DLL模块例2:使用__declspec(dllexport)导出函数,extern "C"规范修饰名称,隐式连接调用dll中函数

    以下内容,我看了多篇文章,整合在一起,写的一个例子,关于dll工程的创建,请参考博客里另一篇文章:http://www.cnblogs.com/pingge/articles/3153571.html ...

  6. DLL的导出函数重定向机制

    曾经,调试时跟进HeapAlloc,结果发现直接进入到ntdll的RtlAllocateHeap中,感到很有趣,就使用Dependency Walker查看kernel32.dll的导出函数,结果发现 ...

  7. AFX_MANAGE_STATE(AfxGetStaticModuleState())DLL导出函数包含MFC资源

    AFX_MANAGE_STATE(AfxGetStaticModuleState()) 先看一个例子: .创建一个动态链接到MFC DLL的规则DLL,其内部包含一个对话框资源.指定该对话框ID如下: ...

  8. dll 导出函数名的那些事

    dll 导出函数名的那些事 关键字: VC++  DLL  导出函数 经常使用VC6的Dependency或者是Depends工具查看DLL导出函数的名字,会发现有DLL导出函数的名字有时大不相同,导 ...

  9. C# 遍历DLL导出函数

    C#如何去遍历一个由C++或E语言编写的本地DLL导出函数呢 不过在这里我建议对PE一无所知的人 你或许应先补补这方面的知识,我不知道为什么PE方面的 应用在C#中怎么这么少,我查阅过相关 C#的知识 ...

随机推荐

  1. 学习easyui疑问(三)

    今天我学习easyui中碰到的还有一问题是:怎样创建一个表格? 首先,在easyui中文官网上提供的这样一种定义方式: <!--table--> <table id="tt ...

  2. js中#代表什么

    js中#代表什么 一.总结 1.#号:代表id选择器  2. $('#div1'). : 常用用法,前面也有$符号 二."#"在js中代表什么 js里我不曾看到用到‘#’的代码端, ...

  3. iOS_01_C语言简介

    1.先学C语言的原因 * oc基于C. * oc 跟 C的思想和语法很多地方不太一样,而且OC能和C混用. * C 是所有编程语言中的经典,很多高级语言都是从C语言中衍生出来的,比如 C++,C#.O ...

  4. unmapping error

    否则,会映射一个Getch的器件,就会报unmapping 的error

  5. P2P系统哪家强,功能其实都一样

    现在的P2P平台有好几千家了,了解了其中的几十家,发现用户端的P2P界面功能都差不多.下面来做个简要的总结: 1.通用功能  注册.登录  2.投资理财  针对理财人的投标.债权转让  3.借款申请  ...

  6. 推荐一下《聊聊JVM》的专栏

    依照惯例新开了一个专栏后要单推一下.推荐一下<聊聊JVM的专栏>,网上关于JVM的文章太多,这个专栏希望能在已有的资料的基础上写出点新意,对一些重要的概念归纳总结,说说自己的观点.理解和实 ...

  7. spark提交应用的方法(spark-submit)

    参考自:https://spark.apache.org/docs/latest/submitting-applications.html 常见的语法: ./bin/spark-submit \    ...

  8. 一文看懂AI芯片竞争五大维度

    下一波大趋势和大红利从互联网+让位于人工智能+,已成业界共识.在AI的数据.算法和芯片之三剑客中,考虑到AI算法开源的发展趋势,数据与芯片将占据越来越重要的地位,而作为AI发展支柱的芯片更是AI业的竞 ...

  9. angular2的编译模式之AOT和JIT

    原文 https://www.jianshu.com/p/c959d90e91ce 大纲 1.angular应用为什么需要编译 2.angular的编译模式类型 3.JIT(Just-In-Time) ...

  10. 那些移动端web踩过的坑2

    原文链接:https://geniuspeng.github.io/2018/04/26/mobile-issues2/ 坑是无穷无尽的,嗯-长江后坑推前坑~~ 关于音频自动播放 H5的audio标签 ...